1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
32 #include "deffuncname.hxx"
34 using namespace ::com::sun::star
;
36 constexpr OUString ADDIN_SERVICE
= u
"com.sun.star.sheet.AddIn"_ustr
;
37 constexpr OUString MY_SERVICE
= u
"com.sun.star.sheet.addin.DateFunctions"_ustr
;
38 constexpr OUStringLiteral MY_IMPLNAME
= u
"com.sun.star.sheet.addin.DateFunctionsImpl";
40 #define UNIQUE false // function name does not exist in Calc
42 #define STDPAR false // all parameters are described
43 #define INTPAR true // first parameter is internal
45 #define FUNCDATA( FuncName, ParamCount, Category, Double, IntPar ) \
46 { "get" #FuncName, DATE_FUNCNAME_##FuncName, DATE_FUNCDESC_##FuncName, DATE_DEFFUNCNAME_##FuncName, ParamCount, Category, Double, IntPar }
48 const ScaFuncDataBase pFuncDataArr
[] =
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
)
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
)
71 aCompList
.push_back(OUString::createFromAscii(rBaseData
.pCompListID
[0]));
72 aCompList
.push_back(OUString::createFromAscii(rBaseData
.pCompListID
[1]));
75 sal_uInt16
ScaFuncData::GetStrIndex( sal_uInt16 nParam
) const
79 return (nParam
> nParamCount
) ? (nParamCount
* 2) : (nParam
* 2);
82 static void InitScaFuncDataList(ScaFuncDataList
& rList
)
84 for (const auto & nIndex
: pFuncDataArr
)
85 rList
.emplace_back(nIndex
);
88 // entry points for service registration / instantiation
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&)
94 return cppu::acquire(new ScaDateAddIn());
98 // "normal" service implementation
99 ScaDateAddIn::ScaDateAddIn()
103 static const char* pLang
[] = { "de", "en" };
104 static const char* pCoun
[] = { "DE", "US" };
105 constexpr sal_uInt32 nNumOfLoc
= std::size( pLang
);
107 void ScaDateAddIn::InitDefLocales()
109 pDefLocales
.reset(new lang::Locale
[ nNumOfLoc
]);
111 for( sal_uInt32 nIndex
= 0; nIndex
< nNumOfLoc
; nIndex
++ )
113 pDefLocales
[ nIndex
].Language
= OUString::createFromAscii( pLang
[ nIndex
] );
114 pDefLocales
[ nIndex
].Country
= OUString::createFromAscii( pCoun
[ nIndex
] );
118 const lang::Locale
& ScaDateAddIn::GetLocale( sal_uInt32 nIndex
)
123 return (nIndex
< nNumOfLoc
) ? pDefLocales
[ nIndex
] : aFuncLoc
;
126 void ScaDateAddIn::InitData()
128 aResLocale
= Translate::Create("sca", LanguageTag(aFuncLoc
));
129 pFuncDataList
.reset();
131 pFuncDataList
.reset(new ScaFuncDataList
);
132 InitScaFuncDataList(*pFuncDataList
);
140 OUString
ScaDateAddIn::GetFuncDescrStr(const TranslateId
* pResId
, sal_uInt16 nStrIndex
)
142 return ScaResId(pResId
[nStrIndex
- 1]);
146 OUString SAL_CALL
ScaDateAddIn::getServiceName()
148 // name of specific AddIn service
153 OUString SAL_CALL
ScaDateAddIn::getImplementationName()
158 sal_Bool SAL_CALL
ScaDateAddIn::supportsService( const OUString
& aServiceName
)
160 return cppu::supportsService(this, aServiceName
);
163 uno::Sequence
< OUString
> SAL_CALL
ScaDateAddIn::getSupportedServiceNames()
165 return { ADDIN_SERVICE
, MY_SERVICE
};
169 void SAL_CALL
ScaDateAddIn::setLocale( const lang::Locale
& eLocale
)
172 InitData(); // change of locale invalidates resources!
175 lang::Locale SAL_CALL
ScaDateAddIn::getLocale()
180 OUString SAL_CALL
ScaDateAddIn::getProgrammaticFuntionName( const OUString
& )
183 // (but should be implemented for other uses of the AddIn service)
187 OUString SAL_CALL
ScaDateAddIn::getDisplayFunctionName( const OUString
& aProgrammaticName
)
191 auto fDataIt
= std::find_if(pFuncDataList
->begin(), pFuncDataList
->end(),
192 FindScaFuncData( aProgrammaticName
) );
193 if( fDataIt
!= pFuncDataList
->end() )
195 aRet
= ScaResId(fDataIt
->GetUINameID());
196 if( fDataIt
->IsDouble() )
201 aRet
= "UNKNOWNFUNC_" + aProgrammaticName
;
207 OUString SAL_CALL
ScaDateAddIn::getFunctionDescription( const OUString
& aProgrammaticName
)
211 auto fDataIt
= std::find_if(pFuncDataList
->begin(), pFuncDataList
->end(),
212 FindScaFuncData( aProgrammaticName
) );
213 if( fDataIt
!= pFuncDataList
->end() )
214 aRet
= GetFuncDescrStr( fDataIt
->GetDescrID(), 1 );
219 OUString SAL_CALL
ScaDateAddIn::getDisplayArgumentName(
220 const OUString
& aProgrammaticName
, sal_Int32 nArgument
)
224 auto fDataIt
= std::find_if(pFuncDataList
->begin(), pFuncDataList
->end(),
225 FindScaFuncData( aProgrammaticName
) );
226 if( fDataIt
!= pFuncDataList
->end() && (nArgument
<= 0xFFFF) )
228 sal_uInt16 nStr
= fDataIt
->GetStrIndex( static_cast< sal_uInt16
>( nArgument
) );
230 aRet
= GetFuncDescrStr( fDataIt
->GetDescrID(), nStr
);
238 OUString SAL_CALL
ScaDateAddIn::getArgumentDescription(
239 const OUString
& aProgrammaticName
, sal_Int32 nArgument
)
243 auto fDataIt
= std::find_if(pFuncDataList
->begin(), pFuncDataList
->end(),
244 FindScaFuncData( aProgrammaticName
) );
245 if( fDataIt
!= pFuncDataList
->end() && (nArgument
<= 0xFFFF) )
247 sal_uInt16 nStr
= fDataIt
->GetStrIndex( static_cast< sal_uInt16
>( nArgument
) );
249 aRet
= GetFuncDescrStr( fDataIt
->GetDescrID(), nStr
+ 1 );
251 aRet
= "for internal use only";
257 OUString SAL_CALL
ScaDateAddIn::getProgrammaticCategoryName(
258 const OUString
& aProgrammaticName
)
262 auto fDataIt
= std::find_if(pFuncDataList
->begin(), pFuncDataList
->end(),
263 FindScaFuncData( aProgrammaticName
) );
264 if( fDataIt
!= pFuncDataList
->end() )
266 switch( fDataIt
->GetCategory() )
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;
282 OUString SAL_CALL
ScaDateAddIn::getDisplayCategoryName(
283 const OUString
& aProgrammaticName
)
285 return getProgrammaticCategoryName( aProgrammaticName
);
288 // XCompatibilityNames
289 uno::Sequence
< sheet::LocalizedName
> SAL_CALL
ScaDateAddIn::getCompatibilityNames(
290 const OUString
& aProgrammaticName
)
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 );
297 const std::vector
<OUString
>& rStrList
= fDataIt
->GetCompNameList();
298 sal_uInt32 nCount
= rStrList
.size();
300 uno::Sequence
< sheet::LocalizedName
> aRet( nCount
);
301 sheet::LocalizedName
* pArray
= aRet
.getArray();
303 for( sal_uInt32 nIndex
= 0; nIndex
< nCount
; nIndex
++ )
304 pArray
[ nIndex
] = sheet::LocalizedName( GetLocale( nIndex
), rStrList
.at( nIndex
) );
311 // auxiliary functions
312 bool IsLeapYear( sal_uInt16 nYear
)
314 return ((((nYear
% 4) == 0) && ((nYear
% 100) != 0)) || ((nYear
% 400) == 0));
317 sal_uInt16
DaysInMonth( sal_uInt16 nMonth
, sal_uInt16 nYear
)
319 static const sal_uInt16 aDaysInMonth
[12] = { 31, 28, 31, 30, 31, 30,
320 31, 31, 30, 31, 30, 31 };
323 return aDaysInMonth
[nMonth
-1];
326 if ( IsLeapYear(nYear
) )
327 return aDaysInMonth
[nMonth
-1] + 1;
329 return aDaysInMonth
[nMonth
-1];
334 * Convert a date to a count of days starting from 01/01/0001
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.
342 sal_Int32
DateToDays( sal_uInt16 nDay
, sal_uInt16 nMonth
, sal_uInt16 nYear
)
344 sal_Int32 nDays
= (static_cast<sal_Int32
>(nYear
)-1) * 365;
345 nDays
+= ((nYear
-1) / 4) - ((nYear
-1) / 100) + ((nYear
-1) / 400);
347 for( sal_uInt16 i
= 1; i
< nMonth
; i
++ )
348 nDays
+= DaysInMonth(i
,nYear
);
355 * Convert a count of days starting from 01/01/0001 to a date
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.
362 * @throws lang::IllegalArgumentException
365 void DaysToDate( sal_Int32 nDays
,
366 sal_uInt16
& rDay
, sal_uInt16
& rMonth
, sal_uInt16
& rYear
)
369 throw lang::IllegalArgumentException();
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);
389 if ( nTempDays
> 365 )
391 if ( (nTempDays
!= 366) || !IsLeapYear( rYear
) )
402 while ( nTempDays
> DaysInMonth( rMonth
, rYear
) )
404 nTempDays
-= DaysInMonth( rMonth
, rYear
);
407 rDay
= static_cast<sal_uInt16
>(nTempDays
);
411 * Get the null date used by the spreadsheet document
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
417 * @throws uno::RuntimeException
419 sal_Int32
GetNullDate( const uno::Reference
< beans::XPropertySet
>& xOptions
)
425 uno::Any aAny
= xOptions
->getPropertyValue( u
"NullDate"_ustr
);
427 if ( aAny
>>= aDate
)
428 return DateToDays( aDate
.Day
, aDate
.Month
, aDate
.Year
);
430 catch (uno::Exception
&)
435 // no null date available -> no calculations possible
436 throw uno::RuntimeException();
443 * Get week difference between 2 dates
445 * new Weeks(date1,date2,mode) function for StarCalc
447 * Two modes of operation are provided.
448 * mode 0 is just a simple division by 7 calculation.
450 * mode 1 calculates the difference by week adhering to ISO8601.
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.
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.
461 * Weeks(d2,d1,m) is defined as -Weeks(d1,d2,m).
465 sal_Int32 SAL_CALL
ScaDateAddIn::getDiffWeeks(
466 const uno::Reference
< beans::XPropertySet
>& xOptions
,
467 sal_Int32 nStartDate
, sal_Int32 nEndDate
,
472 return ( nEndDate
- nStartDate
) / 7;
474 else if ( nMode
== 1 )
476 sal_Int32 nNullDate
= GetNullDate( xOptions
);
477 sal_Int32 nDays1
= nStartDate
+ nNullDate
- 1;
478 sal_Int32 nDays2
= nEndDate
+ nNullDate
- 1;
480 return ( std::floor( nDays2
/ 7.0 ) - std::floor( nDays1
/ 7.0 ) );
483 throw lang::IllegalArgumentException();
487 * Get month difference between 2 dates
488 * =Month(start, end, mode) Function for StarCalc
490 * two modes are provided
492 * mode 0 is the interval between the dates in month
494 * mode 1 is the difference in calendar month
496 sal_Int32 SAL_CALL
ScaDateAddIn::getDiffMonths(
497 const uno::Reference
< beans::XPropertySet
>& xOptions
,
498 sal_Int32 nStartDate
, sal_Int32 nEndDate
,
501 if (nMode
!= 0 && nMode
!= 1)
502 throw lang::IllegalArgumentException();
504 sal_Int32 nNullDate
= GetNullDate( xOptions
);
506 sal_Int32 nDays1
= nStartDate
+ nNullDate
;
507 sal_Int32 nDays2
= nEndDate
+ nNullDate
;
509 sal_uInt16 nDay1
,nMonth1
,nYear1
;
510 sal_uInt16 nDay2
,nMonth2
,nYear2
;
511 DaysToDate(nDays1
,nDay1
,nMonth1
,nYear1
);
512 DaysToDate(nDays2
,nDay2
,nMonth2
,nYear2
);
514 sal_Int32 nRet
= nMonth2
- nMonth1
+ (nYear2
- nYear1
) * 12;
515 if ( nMode
== 1 || nDays1
== nDays2
) return nRet
;
517 if ( nDays1
< nDays2
)
536 * Get Year difference between 2 dates
538 * two modes are provided
540 * mode 0 is the interval between the dates in years
542 * mode 1 is the difference in calendar years
544 sal_Int32 SAL_CALL
ScaDateAddIn::getDiffYears(
545 const uno::Reference
< beans::XPropertySet
>& xOptions
,
546 sal_Int32 nStartDate
, sal_Int32 nEndDate
,
549 if (nMode
!= 0 && nMode
!= 1)
550 throw lang::IllegalArgumentException();
553 return getDiffMonths( xOptions
, nStartDate
, nEndDate
, nMode
) / 12;
555 sal_Int32 nNullDate
= GetNullDate( xOptions
);
557 sal_Int32 nDays1
= nStartDate
+ nNullDate
;
558 sal_Int32 nDays2
= nEndDate
+ nNullDate
;
560 sal_uInt16 nDay1
,nMonth1
,nYear1
;
561 sal_uInt16 nDay2
,nMonth2
,nYear2
;
562 DaysToDate(nDays1
,nDay1
,nMonth1
,nYear1
);
563 DaysToDate(nDays2
,nDay2
,nMonth2
,nYear2
);
565 return nYear2
- nYear1
;
569 * Check if a Date is in a leap year in the Gregorian calendar
571 sal_Int32 SAL_CALL
ScaDateAddIn::getIsLeapYear(
572 const uno::Reference
< beans::XPropertySet
>& xOptions
,
575 sal_Int32 nNullDate
= GetNullDate( xOptions
);
576 sal_Int32 nDays
= nDate
+ nNullDate
;
578 sal_uInt16 nDay
, nMonth
, nYear
;
579 DaysToDate(nDays
,nDay
,nMonth
,nYear
);
581 return static_cast<sal_Int32
>(IsLeapYear(nYear
));
585 * Get the Number of Days in the month for a date
587 sal_Int32 SAL_CALL
ScaDateAddIn::getDaysInMonth(
588 const uno::Reference
<beans::XPropertySet
>& xOptions
,
591 sal_Int32 nNullDate
= GetNullDate( xOptions
);
592 sal_Int32 nDays
= nDate
+ nNullDate
;
594 sal_uInt16 nDay
, nMonth
, nYear
;
595 DaysToDate(nDays
,nDay
,nMonth
,nYear
);
597 return DaysInMonth( nMonth
, nYear
);
601 * Get number of days in the year of a date specified
603 sal_Int32 SAL_CALL
ScaDateAddIn::getDaysInYear(
604 const uno::Reference
< beans::XPropertySet
>& xOptions
,
607 sal_Int32 nNullDate
= GetNullDate( xOptions
);
608 sal_Int32 nDays
= nDate
+ nNullDate
;
610 sal_uInt16 nDay
, nMonth
, nYear
;
611 DaysToDate(nDays
,nDay
,nMonth
,nYear
);
613 return ( IsLeapYear(nYear
) ? 366 : 365 );
617 * Get number of weeks in the year for a date
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
622 * The International Standard IS-8601 has decreed that Monday
623 * shall be the first day of the week.
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
629 * @see #IsLeapYear #WeekNumber
631 sal_Int32 SAL_CALL
ScaDateAddIn::getWeeksInYear(
632 const uno::Reference
< beans::XPropertySet
>& xOptions
,
635 sal_Int32 nNullDate
= GetNullDate( xOptions
);
636 sal_Int32 nDays
= nDate
+ nNullDate
;
638 sal_uInt16 nDay
, nMonth
, nYear
;
639 DaysToDate(nDays
,nDay
,nMonth
,nYear
);
641 sal_Int32 nJan1WeekDay
= ( DateToDays(1,1,nYear
) - 1) % 7;
644 if ( nJan1WeekDay
== 3 ) /* Thursday */
646 else if ( nJan1WeekDay
== 2 ) /* Wednesday */
647 nRet
= ( IsLeapYear(nYear
) ? 53 : 52 );
655 * Encrypt or decrypt a string using ROT13 algorithm
657 * This function rotates each character by 13 in the alphabet.
658 * Only the characters 'a' ... 'z' and 'A' ... 'Z' are modified.
660 OUString SAL_CALL
ScaDateAddIn::getRot13( const OUString
& aSrcString
)
662 OUStringBuffer
aBuffer( aSrcString
);
663 for( sal_Int32 nIndex
= 0; nIndex
< aBuffer
.getLength(); nIndex
++ )
665 sal_Unicode cChar
= aBuffer
[nIndex
];
666 if( (cChar
>= 'a') && (cChar
<= 'z'))
672 else if( (cChar
>= 'A') && (cChar
<= 'Z') )
678 aBuffer
[nIndex
] = cChar
;
680 return aBuffer
.makeStringAndClear();
683 OUString
ScaDateAddIn::ScaResId(TranslateId aId
)
685 return Translate::get(aId
, aResLocale
);
688 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */