Avoid a warning shown since bumping Windows baseline
[LibreOffice.git] / scaddins / source / datefunc / datefunc.cxx
blob07ecd46c6046d41129a8b828ea2041d8f685cc55
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
30 #include <algorithm>
31 #include <cmath>
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 )
60 #undef FUNCDATA
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
77 if( !bWithOpt )
78 nParam++;
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 )
120 if( !pDefLocales )
121 InitDefLocales();
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);
134 if( pDefLocales )
136 pDefLocales.reset();
140 OUString ScaDateAddIn::GetFuncDescrStr(const TranslateId* pResId, sal_uInt16 nStrIndex)
142 return ScaResId(pResId[nStrIndex - 1]);
145 // XServiceName
146 OUString SAL_CALL ScaDateAddIn::getServiceName()
148 // name of specific AddIn service
149 return MY_SERVICE;
152 // XServiceInfo
153 OUString SAL_CALL ScaDateAddIn::getImplementationName()
155 return MY_IMPLNAME;
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 };
168 // XLocalizable
169 void SAL_CALL ScaDateAddIn::setLocale( const lang::Locale& eLocale )
171 aFuncLoc = eLocale;
172 InitData(); // change of locale invalidates resources!
175 lang::Locale SAL_CALL ScaDateAddIn::getLocale()
177 return aFuncLoc;
180 OUString SAL_CALL ScaDateAddIn::getProgrammaticFuntionName( const OUString& )
182 // not used by calc
183 // (but should be implemented for other uses of the AddIn service)
184 return OUString();
187 OUString SAL_CALL ScaDateAddIn::getDisplayFunctionName( const OUString& aProgrammaticName )
189 OUString aRet;
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() )
197 aRet += "_ADD";
199 else
201 aRet = "UNKNOWNFUNC_" + aProgrammaticName;
204 return aRet;
207 OUString SAL_CALL ScaDateAddIn::getFunctionDescription( const OUString& aProgrammaticName )
209 OUString aRet;
211 auto fDataIt = std::find_if(pFuncDataList->begin(), pFuncDataList->end(),
212 FindScaFuncData( aProgrammaticName ) );
213 if( fDataIt != pFuncDataList->end() )
214 aRet = GetFuncDescrStr( fDataIt->GetDescrID(), 1 );
216 return aRet;
219 OUString SAL_CALL ScaDateAddIn::getDisplayArgumentName(
220 const OUString& aProgrammaticName, sal_Int32 nArgument )
222 OUString aRet;
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 ) );
229 if( nStr )
230 aRet = GetFuncDescrStr( fDataIt->GetDescrID(), nStr );
231 else
232 aRet = "internal";
235 return aRet;
238 OUString SAL_CALL ScaDateAddIn::getArgumentDescription(
239 const OUString& aProgrammaticName, sal_Int32 nArgument )
241 OUString aRet;
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 ) );
248 if( nStr )
249 aRet = GetFuncDescrStr( fDataIt->GetDescrID(), nStr + 1 );
250 else
251 aRet = "for internal use only";
254 return aRet;
257 OUString SAL_CALL ScaDateAddIn::getProgrammaticCategoryName(
258 const OUString& aProgrammaticName )
260 OUString aRet;
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;
277 if( aRet.isEmpty() )
278 aRet = "Add-In";
279 return aRet;
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 ) );
306 return aRet;
309 namespace {
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 };
322 if ( nMonth != 2 )
323 return aDaysInMonth[nMonth-1];
324 else
326 if ( IsLeapYear(nYear) )
327 return aDaysInMonth[nMonth-1] + 1;
328 else
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);
349 nDays += nDay;
351 return nDays;
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 )
368 if( nDays < 0 )
369 throw lang::IllegalArgumentException();
371 sal_Int32 nTempDays;
372 sal_Int32 i = 0;
373 bool bCalc;
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 )
384 i++;
385 bCalc = true;
387 else
389 if ( nTempDays > 365 )
391 if ( (nTempDays != 366) || !IsLeapYear( rYear ) )
393 i--;
394 bCalc = true;
399 while ( bCalc );
401 rMonth = 1;
402 while ( nTempDays > DaysInMonth( rMonth, rYear ) )
404 nTempDays -= DaysInMonth( rMonth, rYear );
405 rMonth++;
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 )
421 if (xOptions.is())
425 uno::Any aAny = xOptions->getPropertyValue( u"NullDate"_ustr );
426 util::Date aDate;
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();
440 // XDateFunctions
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,
468 sal_Int32 nMode )
470 if ( nMode == 0 )
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 ) );
482 else
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,
499 sal_Int32 nMode )
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 )
519 if ( nDay1 > nDay2 )
521 nRet -= 1;
524 else
526 if ( nDay1 < nDay2 )
528 nRet += 1;
532 return nRet;
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,
547 sal_Int32 nMode )
549 if (nMode != 0 && nMode != 1)
550 throw lang::IllegalArgumentException();
552 if ( nMode != 1 )
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,
573 sal_Int32 nDate )
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,
589 sal_Int32 nDate )
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,
605 sal_Int32 nDate )
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,
633 sal_Int32 nDate )
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;
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;
651 return nRet;
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'))
668 cChar += 13;
669 if (cChar > 'z')
670 cChar -= 26;
672 else if( (cChar >= 'A') && (cChar <= 'Z') )
674 cChar += 13;
675 if (cChar > 'Z')
676 cChar -= 26;
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: */