1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "datefunc.hxx"
21 #include <datefunc.hrc>
22 #include <strings.hrc>
23 #include <com/sun/star/util/Date.hpp>
24 #include <cppuhelper/factory.hxx>
25 #include <cppuhelper/supportsservice.hxx>
26 #include <cppuhelper/weak.hxx>
27 #include <rtl/ustrbuf.hxx>
28 #include <unotools/resmgr.hxx>
29 #include <i18nlangtag/languagetag.hxx>
30 #include <algorithm>
31 #include "deffuncname.hxx"
32 
33 using namespace ::com::sun::star;
34 
35 #define ADDIN_SERVICE           "com.sun.star.sheet.AddIn"
36 #define MY_SERVICE              "com.sun.star.sheet.addin.DateFunctions"
37 #define MY_IMPLNAME             "com.sun.star.sheet.addin.DateFunctionsImpl"
38 
39 #define UNIQUE              false   // function name does not exist in Calc
40 
41 #define STDPAR              false   // all parameters are described
42 #define INTPAR              true    // first parameter is internal
43 
44 #define FUNCDATA( FuncName, ParamCount, Category, Double, IntPar )  \
45     { "get" #FuncName, DATE_FUNCNAME_##FuncName, DATE_FUNCDESC_##FuncName, DATE_DEFFUNCNAME_##FuncName, ParamCount, Category, Double, IntPar }
46 
47 const ScaFuncDataBase pFuncDataArr[] =
48 {
49     FUNCDATA( DiffWeeks,    3, ScaCategory::DateTime, UNIQUE, INTPAR ),
50     FUNCDATA( DiffMonths,   3, ScaCategory::DateTime, UNIQUE, INTPAR ),
51     FUNCDATA( DiffYears,    3, ScaCategory::DateTime, UNIQUE, INTPAR ),
52     FUNCDATA( IsLeapYear,   1, ScaCategory::DateTime, UNIQUE, INTPAR ),
53     FUNCDATA( DaysInMonth,  1, ScaCategory::DateTime, UNIQUE, INTPAR ),
54     FUNCDATA( DaysInYear,   1, ScaCategory::DateTime, UNIQUE, INTPAR ),
55     FUNCDATA( WeeksInYear,  1, ScaCategory::DateTime, UNIQUE, INTPAR ),
56     FUNCDATA( Rot13,        1, ScaCategory::Text,     UNIQUE, STDPAR )
57 };
58 
59 #undef FUNCDATA
60 
ScaFuncData(const ScaFuncDataBase & rBaseData)61 ScaFuncData::ScaFuncData(const ScaFuncDataBase& rBaseData) :
62     aIntName( OUString::createFromAscii( rBaseData.pIntName ) ),
63     pUINameID( rBaseData.pUINameID ),
64     pDescrID( rBaseData.pDescrID ),
65     nParamCount( rBaseData.nParamCount ),
66     eCat( rBaseData.eCat ),
67     bDouble( rBaseData.bDouble ),
68     bWithOpt( rBaseData.bWithOpt )
69 {
70     aCompList.push_back(OUString::createFromAscii(rBaseData.pCompListID[0]));
71     aCompList.push_back(OUString::createFromAscii(rBaseData.pCompListID[1]));
72 }
73 
GetStrIndex(sal_uInt16 nParam) const74 sal_uInt16 ScaFuncData::GetStrIndex( sal_uInt16 nParam ) const
75 {
76     if( !bWithOpt )
77         nParam++;
78     return (nParam > nParamCount) ? (nParamCount * 2) : (nParam * 2);
79 }
80 
InitScaFuncDataList(ScaFuncDataList & rList)81 static void InitScaFuncDataList(ScaFuncDataList& rList)
82 {
83     for (const auto & nIndex : pFuncDataArr)
84         rList.push_back(ScaFuncData(nIndex));
85 }
86 
87 //  entry points for service registration / instantiation
88 
89 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
scaddins_ScaDateAddIn_get_implementation(css::uno::XComponentContext *,css::uno::Sequence<css::uno::Any> const &)90 scaddins_ScaDateAddIn_get_implementation(
91     css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
92 {
93     return cppu::acquire(new ScaDateAddIn());
94 }
95 
96 
97 //  "normal" service implementation
ScaDateAddIn()98 ScaDateAddIn::ScaDateAddIn()
99 {
100 }
101 
102 static const char*  pLang[] = { "de", "en" };
103 static const char*  pCoun[] = { "DE", "US" };
104 const sal_uInt32 nNumOfLoc = SAL_N_ELEMENTS( pLang );
105 
InitDefLocales()106 void ScaDateAddIn::InitDefLocales()
107 {
108     pDefLocales.reset(new lang::Locale[ nNumOfLoc ]);
109 
110     for( sal_uInt32 nIndex = 0; nIndex < nNumOfLoc; nIndex++ )
111     {
112         pDefLocales[ nIndex ].Language = OUString::createFromAscii( pLang[ nIndex ] );
113         pDefLocales[ nIndex ].Country = OUString::createFromAscii( pCoun[ nIndex ] );
114     }
115 }
116 
GetLocale(sal_uInt32 nIndex)117 const lang::Locale& ScaDateAddIn::GetLocale( sal_uInt32 nIndex )
118 {
119     if( !pDefLocales )
120         InitDefLocales();
121 
122     return (nIndex < sizeof( pLang )) ? pDefLocales[ nIndex ] : aFuncLoc;
123 }
124 
InitData()125 void ScaDateAddIn::InitData()
126 {
127     aResLocale = Translate::Create("sca", LanguageTag(aFuncLoc));
128     pFuncDataList.reset();
129 
130     pFuncDataList.reset(new ScaFuncDataList);
131     InitScaFuncDataList(*pFuncDataList);
132 
133     if( pDefLocales )
134     {
135         pDefLocales.reset();
136     }
137 }
138 
GetFuncDescrStr(const char ** pResId,sal_uInt16 nStrIndex)139 OUString ScaDateAddIn::GetFuncDescrStr(const char** pResId, sal_uInt16 nStrIndex)
140 {
141     return ScaResId(pResId[nStrIndex - 1]);
142 }
143 
144 // XServiceName
getServiceName()145 OUString SAL_CALL ScaDateAddIn::getServiceName()
146 {
147     // name of specific AddIn service
148     return MY_SERVICE;
149 }
150 
151 // XServiceInfo
getImplementationName()152 OUString SAL_CALL ScaDateAddIn::getImplementationName()
153 {
154     return MY_IMPLNAME;
155 }
156 
supportsService(const OUString & aServiceName)157 sal_Bool SAL_CALL ScaDateAddIn::supportsService( const OUString& aServiceName )
158 {
159     return cppu::supportsService(this, aServiceName);
160 }
161 
getSupportedServiceNames()162 uno::Sequence< OUString > SAL_CALL ScaDateAddIn::getSupportedServiceNames()
163 {
164     return { ADDIN_SERVICE, MY_SERVICE };
165 }
166 
167 // XLocalizable
setLocale(const lang::Locale & eLocale)168 void SAL_CALL ScaDateAddIn::setLocale( const lang::Locale& eLocale )
169 {
170     aFuncLoc = eLocale;
171     InitData();     // change of locale invalidates resources!
172 }
173 
getLocale()174 lang::Locale SAL_CALL ScaDateAddIn::getLocale()
175 {
176     return aFuncLoc;
177 }
178 
getProgrammaticFuntionName(const OUString &)179 OUString SAL_CALL ScaDateAddIn::getProgrammaticFuntionName( const OUString& )
180 {
181     //  not used by calc
182     //  (but should be implemented for other uses of the AddIn service)
183     return OUString();
184 }
185 
getDisplayFunctionName(const OUString & aProgrammaticName)186 OUString SAL_CALL ScaDateAddIn::getDisplayFunctionName( const OUString& aProgrammaticName )
187 {
188     OUString aRet;
189 
190     auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(),
191                                 FindScaFuncData( aProgrammaticName ) );
192     if( fDataIt != pFuncDataList->end() )
193     {
194         aRet = ScaResId(fDataIt->GetUINameID());
195         if( fDataIt->IsDouble() )
196             aRet += "_ADD";
197     }
198     else
199     {
200         aRet = "UNKNOWNFUNC_" + aProgrammaticName;
201     }
202 
203     return aRet;
204 }
205 
getFunctionDescription(const OUString & aProgrammaticName)206 OUString SAL_CALL ScaDateAddIn::getFunctionDescription( const OUString& aProgrammaticName )
207 {
208     OUString aRet;
209 
210     auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(),
211                                 FindScaFuncData( aProgrammaticName ) );
212     if( fDataIt != pFuncDataList->end() )
213         aRet = GetFuncDescrStr( fDataIt->GetDescrID(), 1 );
214 
215     return aRet;
216 }
217 
getDisplayArgumentName(const OUString & aProgrammaticName,sal_Int32 nArgument)218 OUString SAL_CALL ScaDateAddIn::getDisplayArgumentName(
219         const OUString& aProgrammaticName, sal_Int32 nArgument )
220 {
221     OUString aRet;
222 
223     auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(),
224                                 FindScaFuncData( aProgrammaticName ) );
225     if( fDataIt != pFuncDataList->end() && (nArgument <= 0xFFFF) )
226     {
227         sal_uInt16 nStr = fDataIt->GetStrIndex( static_cast< sal_uInt16 >( nArgument ) );
228         if( nStr )
229             aRet = GetFuncDescrStr( fDataIt->GetDescrID(), nStr );
230         else
231             aRet = "internal";
232     }
233 
234     return aRet;
235 }
236 
getArgumentDescription(const OUString & aProgrammaticName,sal_Int32 nArgument)237 OUString SAL_CALL ScaDateAddIn::getArgumentDescription(
238         const OUString& aProgrammaticName, sal_Int32 nArgument )
239 {
240     OUString aRet;
241 
242     auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(),
243                                 FindScaFuncData( aProgrammaticName ) );
244     if( fDataIt != pFuncDataList->end() && (nArgument <= 0xFFFF) )
245     {
246         sal_uInt16 nStr = fDataIt->GetStrIndex( static_cast< sal_uInt16 >( nArgument ) );
247         if( nStr )
248             aRet = GetFuncDescrStr( fDataIt->GetDescrID(), nStr + 1 );
249         else
250             aRet = "for internal use only";
251     }
252 
253     return aRet;
254 }
255 
getProgrammaticCategoryName(const OUString & aProgrammaticName)256 OUString SAL_CALL ScaDateAddIn::getProgrammaticCategoryName(
257         const OUString& aProgrammaticName )
258 {
259     OUString aRet;
260 
261     auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(),
262                                 FindScaFuncData( aProgrammaticName ) );
263     if( fDataIt != pFuncDataList->end() )
264     {
265         switch( fDataIt->GetCategory() )
266         {
267             case ScaCategory::DateTime:   aRet = "Date&Time";    break;
268             case ScaCategory::Text:       aRet = "Text";         break;
269             case ScaCategory::Finance:    aRet = "Financial";    break;
270             case ScaCategory::Inf:        aRet = "Information";  break;
271             case ScaCategory::Math:       aRet = "Mathematical"; break;
272             case ScaCategory::Tech:       aRet = "Technical";    break;
273         }
274     }
275 
276     if( aRet.isEmpty() )
277         aRet = "Add-In";
278     return aRet;
279 }
280 
getDisplayCategoryName(const OUString & aProgrammaticName)281 OUString SAL_CALL ScaDateAddIn::getDisplayCategoryName(
282         const OUString& aProgrammaticName )
283 {
284     return getProgrammaticCategoryName( aProgrammaticName );
285 }
286 
287 // XCompatibilityNames
getCompatibilityNames(const OUString & aProgrammaticName)288 uno::Sequence< sheet::LocalizedName > SAL_CALL ScaDateAddIn::getCompatibilityNames(
289         const OUString& aProgrammaticName )
290 {
291     auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(),
292                                 FindScaFuncData( aProgrammaticName ) );
293     if( fDataIt == pFuncDataList->end() )
294         return uno::Sequence< sheet::LocalizedName >( 0 );
295 
296     const std::vector<OUString>& rStrList = fDataIt->GetCompNameList();
297     sal_uInt32 nCount = rStrList.size();
298 
299     uno::Sequence< sheet::LocalizedName > aRet( nCount );
300     sheet::LocalizedName* pArray = aRet.getArray();
301 
302     for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
303         pArray[ nIndex ] = sheet::LocalizedName( GetLocale( nIndex ), rStrList.at( nIndex ) );
304 
305     return aRet;
306 }
307 
308 namespace {
309 
310 // auxiliary functions
IsLeapYear(sal_uInt16 nYear)311 bool IsLeapYear( sal_uInt16 nYear )
312 {
313     return ((((nYear % 4) == 0) && ((nYear % 100) != 0)) || ((nYear % 400) == 0));
314 }
315 
DaysInMonth(sal_uInt16 nMonth,sal_uInt16 nYear)316 sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
317 {
318     static const sal_uInt16 aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30,
319                                         31, 31, 30, 31, 30, 31 };
320 
321     if ( nMonth != 2 )
322         return aDaysInMonth[nMonth-1];
323     else
324     {
325         if ( IsLeapYear(nYear) )
326             return aDaysInMonth[nMonth-1] + 1;
327         else
328             return aDaysInMonth[nMonth-1];
329     }
330 }
331 
332 /**
333  * Convert a date to a count of days starting from 01/01/0001
334  *
335  * The internal representation of a Date used in this Addin
336  * is the number of days between 01/01/0001 and the date
337  * this function converts a Day , Month, Year representation
338  * to this internal Date value.
339  */
340 
DateToDays(sal_uInt16 nDay,sal_uInt16 nMonth,sal_uInt16 nYear)341 sal_Int32 DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
342 {
343     sal_Int32 nDays = (static_cast<sal_Int32>(nYear)-1) * 365;
344     nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);
345 
346     for( sal_uInt16 i = 1; i < nMonth; i++ )
347         nDays += DaysInMonth(i,nYear);
348     nDays += nDay;
349 
350     return nDays;
351 }
352 
353 /**
354  * Convert a count of days starting from 01/01/0001 to a date
355  *
356  * The internal representation of a Date used in this Addin
357  * is the number of days between 01/01/0001 and the date
358  * this function converts this internal Date value
359  * to a Day , Month, Year representation of a Date.
360  *
361  * @throws lang::IllegalArgumentException
362  */
363 
DaysToDate(sal_Int32 nDays,sal_uInt16 & rDay,sal_uInt16 & rMonth,sal_uInt16 & rYear)364 void DaysToDate( sal_Int32 nDays,
365                 sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear )
366 {
367     if( nDays < 0 )
368         throw lang::IllegalArgumentException();
369 
370     sal_Int32   nTempDays;
371     sal_Int32   i = 0;
372     bool    bCalc;
373 
374     do
375     {
376         nTempDays = nDays;
377         rYear = static_cast<sal_uInt16>((nTempDays / 365) - i);
378         nTempDays -= (static_cast<sal_Int32>(rYear) -1) * 365;
379         nTempDays -= (( rYear -1) / 4) - (( rYear -1) / 100) + ((rYear -1) / 400);
380         bCalc = false;
381         if ( nTempDays < 1 )
382         {
383             i++;
384             bCalc = true;
385         }
386         else
387         {
388             if ( nTempDays > 365 )
389             {
390                 if ( (nTempDays != 366) || !IsLeapYear( rYear ) )
391                 {
392                     i--;
393                     bCalc = true;
394                 }
395             }
396         }
397     }
398     while ( bCalc );
399 
400     rMonth = 1;
401     while ( nTempDays > DaysInMonth( rMonth, rYear ) )
402     {
403         nTempDays -= DaysInMonth( rMonth, rYear );
404         rMonth++;
405     }
406     rDay = static_cast<sal_uInt16>(nTempDays);
407 }
408 
409 /**
410  * Get the null date used by the spreadsheet document
411  *
412  * The internal representation of a Date used in this Addin
413  * is the number of days between 01/01/0001 and the date
414  * this function returns this internal Date value for the document null date
415  *
416  * @throws uno::RuntimeException
417  */
GetNullDate(const uno::Reference<beans::XPropertySet> & xOptions)418 sal_Int32 GetNullDate( const uno::Reference< beans::XPropertySet >& xOptions )
419 {
420     if (xOptions.is())
421     {
422         try
423         {
424             uno::Any aAny = xOptions->getPropertyValue( "NullDate" );
425             util::Date aDate;
426             if ( aAny >>= aDate )
427                 return DateToDays( aDate.Day, aDate.Month, aDate.Year );
428         }
429         catch (uno::Exception&)
430         {
431         }
432     }
433 
434     // no null date available -> no calculations possible
435     throw uno::RuntimeException();
436 }
437 
438 }
439 // XDateFunctions
440 
441 /**
442  * Get week difference between 2 dates
443  *
444  * new Weeks(date1,date2,mode) function for StarCalc
445  *
446  * Two modes of operation are provided.
447  * The first is just a simple division by 7 calculation.
448  *
449  * The second calculates the difference by week of year.
450  *
451  * The International Standard IS-8601 has decreed that Monday
452  * shall be the first day of the week.
453  *
454  * A week that lies partly in one year and partly in another
455  * is assigned a number in the year in which most of its days lie.
456  *
457  * That means that week 1 of any year is the week that contains the 4. January
458  *
459  * The internal representation of a Date used in the Addin is the number of days based on 01/01/0001
460  *
461  * A WeekDay can be then calculated by subtracting 1 and calculating the rest of
462  * a division by 7, which gives a 0 - 6 value for Monday - Sunday
463  *
464  * Using the 4. January rule explained above the formula
465  *
466  *  nWeek1= ( nDays1 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1;
467  *
468  * calculates a number between 0-53 for each day which is in the same year as nJan4
469  * where 0 means that this week belonged to the year before.
470  *
471  * If a day in the same or another year is used in this formula this calculates
472  * a calendar week offset from a given 4. January
473  *
474  *  nWeek2 = ( nDays2 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1;
475  *
476  * The 4.January of first Date Argument can thus be used to calculate
477  * the week difference by calendar weeks which is then nWeek = nWeek2 - nWeek1
478  *
479  * which can be optimized to
480  *
481  * nWeek = ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 )
482  *
483  * Note: All calculations are operating on the long integer data type
484  * % is the modulo operator in C which calculates the rest of an Integer division
485  *
486  *
487  * mode 0 is the interval between the dates in month, that is days / 7
488  *
489  * mode 1 is the difference by week of year
490  *
491  */
492 
getDiffWeeks(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nStartDate,sal_Int32 nEndDate,sal_Int32 nMode)493 sal_Int32 SAL_CALL ScaDateAddIn::getDiffWeeks(
494         const uno::Reference< beans::XPropertySet >& xOptions,
495         sal_Int32 nStartDate, sal_Int32 nEndDate,
496         sal_Int32 nMode )
497 {
498     if (nMode != 0 && nMode != 1)
499         throw lang::IllegalArgumentException();
500 
501     sal_Int32 nNullDate = GetNullDate( xOptions );
502 
503     sal_Int32 nDays1 = nStartDate + nNullDate;
504     sal_Int32 nDays2 = nEndDate + nNullDate;
505 
506     sal_Int32 nRet;
507 
508     if ( nMode == 1 )
509     {
510         sal_uInt16 nDay,nMonth,nYear;
511         DaysToDate( nDays1, nDay, nMonth, nYear );
512         sal_Int32 nJan4 = DateToDays( 4, 1, nYear );
513 
514         nRet = ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 );
515     }
516     else
517     {
518         nRet = (nDays2 - nDays1) / 7;
519     }
520     return nRet;
521 }
522 
523 /**
524  * Get month difference between 2 dates
525  * =Month(start, end, mode) Function for StarCalc
526  *
527  * two modes are provided
528  *
529  * mode 0 is the interval between the dates in month
530  *
531  * mode 1 is the difference in calendar month
532  */
getDiffMonths(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nStartDate,sal_Int32 nEndDate,sal_Int32 nMode)533 sal_Int32 SAL_CALL ScaDateAddIn::getDiffMonths(
534         const uno::Reference< beans::XPropertySet >& xOptions,
535         sal_Int32 nStartDate, sal_Int32 nEndDate,
536         sal_Int32 nMode )
537 {
538     if (nMode != 0 && nMode != 1)
539         throw lang::IllegalArgumentException();
540 
541     sal_Int32 nNullDate = GetNullDate( xOptions );
542 
543     sal_Int32 nDays1 = nStartDate + nNullDate;
544     sal_Int32 nDays2 = nEndDate + nNullDate;
545 
546     sal_uInt16 nDay1,nMonth1,nYear1;
547     sal_uInt16 nDay2,nMonth2,nYear2;
548     DaysToDate(nDays1,nDay1,nMonth1,nYear1);
549     DaysToDate(nDays2,nDay2,nMonth2,nYear2);
550 
551     sal_Int32 nRet = nMonth2 - nMonth1 + (nYear2 - nYear1) * 12;
552     if ( nMode == 1 || nDays1 == nDays2 ) return nRet;
553 
554     if ( nDays1 < nDays2 )
555     {
556         if ( nDay1 > nDay2 )
557         {
558             nRet -= 1;
559         }
560     }
561     else
562     {
563         if ( nDay1 < nDay2 )
564         {
565             nRet += 1;
566         }
567     }
568 
569     return nRet;
570 }
571 
572 /**
573  * Get Year difference between 2 dates
574  *
575  * two modes are provided
576  *
577  * mode 0 is the interval between the dates in years
578  *
579  * mode 1 is the difference in calendar years
580  */
getDiffYears(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nStartDate,sal_Int32 nEndDate,sal_Int32 nMode)581 sal_Int32 SAL_CALL ScaDateAddIn::getDiffYears(
582         const uno::Reference< beans::XPropertySet >& xOptions,
583         sal_Int32 nStartDate, sal_Int32 nEndDate,
584         sal_Int32 nMode )
585 {
586     if (nMode != 0 && nMode != 1)
587         throw lang::IllegalArgumentException();
588 
589     if ( nMode != 1 )
590         return getDiffMonths( xOptions, nStartDate, nEndDate, nMode ) / 12;
591 
592     sal_Int32 nNullDate = GetNullDate( xOptions );
593 
594     sal_Int32 nDays1 = nStartDate + nNullDate;
595     sal_Int32 nDays2 = nEndDate + nNullDate;
596 
597     sal_uInt16 nDay1,nMonth1,nYear1;
598     sal_uInt16 nDay2,nMonth2,nYear2;
599     DaysToDate(nDays1,nDay1,nMonth1,nYear1);
600     DaysToDate(nDays2,nDay2,nMonth2,nYear2);
601 
602     return nYear2 - nYear1;
603 }
604 
605 /**
606  * Check if a Date is in a leap year in the Gregorian calendar
607  */
getIsLeapYear(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate)608 sal_Int32 SAL_CALL ScaDateAddIn::getIsLeapYear(
609         const uno::Reference< beans::XPropertySet >& xOptions,
610         sal_Int32 nDate )
611 {
612     sal_Int32 nNullDate = GetNullDate( xOptions );
613     sal_Int32 nDays = nDate + nNullDate;
614 
615     sal_uInt16 nDay, nMonth, nYear;
616     DaysToDate(nDays,nDay,nMonth,nYear);
617 
618     return static_cast<sal_Int32>(IsLeapYear(nYear));
619 }
620 
621 /**
622  * Get the Number of Days in the month for a date
623  */
getDaysInMonth(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate)624 sal_Int32 SAL_CALL ScaDateAddIn::getDaysInMonth(
625         const uno::Reference<beans::XPropertySet>& xOptions,
626         sal_Int32 nDate )
627 {
628     sal_Int32 nNullDate = GetNullDate( xOptions );
629     sal_Int32 nDays = nDate + nNullDate;
630 
631     sal_uInt16 nDay, nMonth, nYear;
632     DaysToDate(nDays,nDay,nMonth,nYear);
633 
634     return DaysInMonth( nMonth, nYear );
635 }
636 
637 /**
638  * Get number of days in the year of a date specified
639  */
getDaysInYear(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate)640 sal_Int32 SAL_CALL ScaDateAddIn::getDaysInYear(
641         const uno::Reference< beans::XPropertySet >& xOptions,
642         sal_Int32 nDate )
643 {
644     sal_Int32 nNullDate = GetNullDate( xOptions );
645     sal_Int32 nDays = nDate + nNullDate;
646 
647     sal_uInt16 nDay, nMonth, nYear;
648     DaysToDate(nDays,nDay,nMonth,nYear);
649 
650     return ( IsLeapYear(nYear) ? 366 : 365 );
651 }
652 
653 /**
654  * Get number of weeks in the year for a date
655  *
656  * Most years have 52 weeks, but years that start on a Thursday
657  * and leap years that start on a Wednesday have 53 weeks
658  *
659  * The International Standard IS-8601 has decreed that Monday
660  * shall be the first day of the week.
661  *
662  * A WeekDay can be calculated by subtracting 1 and calculating the rest of
663  * a division by 7 from the internal date representation
664  * which gives a 0 - 6 value for Monday - Sunday
665  *
666  * @see #IsLeapYear #WeekNumber
667  */
getWeeksInYear(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate)668 sal_Int32 SAL_CALL ScaDateAddIn::getWeeksInYear(
669         const uno::Reference< beans::XPropertySet >& xOptions,
670         sal_Int32 nDate )
671 {
672     sal_Int32 nNullDate = GetNullDate( xOptions );
673     sal_Int32 nDays = nDate + nNullDate;
674 
675     sal_uInt16 nDay, nMonth, nYear;
676     DaysToDate(nDays,nDay,nMonth,nYear);
677 
678     sal_Int32 nJan1WeekDay = ( DateToDays(1,1,nYear) - 1) % 7;
679 
680     sal_Int32 nRet;
681     if ( nJan1WeekDay == 3 )        /* Thursday */
682         nRet = 53;
683     else if ( nJan1WeekDay == 2 )   /* Wednesday */
684         nRet = ( IsLeapYear(nYear) ? 53 : 52 );
685     else
686         nRet = 52;
687 
688     return nRet;
689 }
690 
691 /**
692  * Encrypt or decrypt a string using ROT13 algorithm
693  *
694  * This function rotates each character by 13 in the alphabet.
695  * Only the characters 'a' ... 'z' and 'A' ... 'Z' are modified.
696  */
getRot13(const OUString & aSrcString)697 OUString SAL_CALL ScaDateAddIn::getRot13( const OUString& aSrcString )
698 {
699     OUStringBuffer aBuffer( aSrcString );
700     for( sal_Int32 nIndex = 0; nIndex < aBuffer.getLength(); nIndex++ )
701     {
702         sal_Unicode cChar = aBuffer[nIndex];
703         if( (cChar >= 'a') && (cChar <= 'z'))
704         {
705             cChar += 13;
706             if (cChar > 'z')
707                 cChar -= 26;
708         }
709         else if( (cChar >= 'A') && (cChar <= 'Z') )
710         {
711             cChar += 13;
712             if (cChar > 'Z')
713                 cChar -= 26;
714         }
715         aBuffer[nIndex] = cChar;
716     }
717     return aBuffer.makeStringAndClear();
718 }
719 
ScaResId(const char * pId)720 OUString ScaDateAddIn::ScaResId(const char* pId)
721 {
722     return Translate::get(pId, aResLocale);
723 }
724 
725 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
726