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