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