tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / scaddins / source / analysis / analysishelper.cxx
blobc8c20fd796728a2a6ae75641ec8232d2dd8f4869
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 <com/sun/star/util/Date.hpp>
21 #include <com/sun/star/util/XNumberFormatTypes.hpp>
22 #include <com/sun/star/util/NumberFormatter.hpp>
24 #include <string.h>
25 #include <stdio.h>
26 #include <o3tl/any.hxx>
27 #include <rtl/math.hxx>
28 #include <algorithm>
29 #include <cmath>
30 #include <complex>
31 #include <memory>
33 #include "analysisdefs.hxx"
34 #include "analysishelper.hxx"
35 #include <analysis.hrc>
36 #include <strings.hrc>
37 #include "deffuncname.hxx"
39 using namespace ::com::sun::star;
40 using namespace sca::analysis;
42 #define UNIQUE false // function name does not exist in Calc
43 #define DOUBLE true // function name exists in Calc
45 #define STDPAR false // all parameters are described
46 #define INTPAR true // first parameter is internal
48 #define INV_MATCHLEV 1764 // guess, what this is... :-) - I doubt this kind of comment looks fun :-(
50 #define FUNCDATA( FUNCNAME, DBL, OPT, NUMOFPAR, CAT ) \
51 { "get" #FUNCNAME, ANALYSIS_FUNCNAME_##FUNCNAME, ANALYSIS_##FUNCNAME, DBL, OPT, ANALYSIS_DEFFUNCNAME_##FUNCNAME, NUMOFPAR, CAT, nullptr }
53 #define FUNCDATAS( FUNCNAME, DBL, OPT, NUMOFPAR, CAT, SUFFIX ) \
54 { "get" #FUNCNAME, ANALYSIS_FUNCNAME_##FUNCNAME, ANALYSIS_##FUNCNAME, DBL, OPT, ANALYSIS_DEFFUNCNAME_##FUNCNAME, NUMOFPAR, CAT, SUFFIX }
56 const FuncDataBase pFuncDatas[] =
58 // UNIQUE or INTPAR or
59 // function name DOUBLE STDPAR # of param category
60 FUNCDATA( Workday, UNIQUE, INTPAR, 3, FDCategory::DateTime ),
61 FUNCDATA( Yearfrac, UNIQUE, INTPAR, 3, FDCategory::DateTime ),
62 FUNCDATA( Edate, UNIQUE, INTPAR, 2, FDCategory::DateTime ),
63 FUNCDATAS( Weeknum, DOUBLE, INTPAR, 2, FDCategory::DateTime, "_EXCEL2003" ),
64 FUNCDATA( Eomonth, UNIQUE, INTPAR, 2, FDCategory::DateTime ),
65 FUNCDATAS( Networkdays, DOUBLE, INTPAR, 3, FDCategory::DateTime, "_EXCEL2003" ),
66 FUNCDATA( Iseven, DOUBLE, STDPAR, 1, FDCategory::Inf ),
67 FUNCDATA( Isodd, DOUBLE, STDPAR, 1, FDCategory::Inf ),
68 FUNCDATA( Multinomial, UNIQUE, STDPAR, 1, FDCategory::Math ),
69 FUNCDATA( Seriessum, UNIQUE, STDPAR, 4, FDCategory::Math ),
70 FUNCDATA( Quotient, UNIQUE, STDPAR, 2, FDCategory::Math ),
71 FUNCDATA( Mround, UNIQUE, STDPAR, 2, FDCategory::Math ),
72 FUNCDATA( Sqrtpi, UNIQUE, STDPAR, 1, FDCategory::Math ),
73 FUNCDATA( Randbetween, UNIQUE, STDPAR, 2, FDCategory::Math ),
74 FUNCDATAS( Gcd, DOUBLE, INTPAR, 1, FDCategory::Math, "_EXCEL2003" ),
75 FUNCDATAS( Lcm, DOUBLE, INTPAR, 1, FDCategory::Math, "_EXCEL2003" ),
76 FUNCDATA( Besseli, UNIQUE, STDPAR, 2, FDCategory::Tech ),
77 FUNCDATA( Besselj, UNIQUE, STDPAR, 2, FDCategory::Tech ),
78 FUNCDATA( Besselk, UNIQUE, STDPAR, 2, FDCategory::Tech ),
79 FUNCDATA( Bessely, UNIQUE, STDPAR, 2, FDCategory::Tech ),
80 FUNCDATA( Bin2Oct, UNIQUE, INTPAR, 2, FDCategory::Tech ),
81 FUNCDATA( Bin2Dec, UNIQUE, STDPAR, 1, FDCategory::Tech ),
82 FUNCDATA( Bin2Hex, UNIQUE, INTPAR, 2, FDCategory::Tech ),
83 FUNCDATA( Oct2Bin, UNIQUE, INTPAR, 2, FDCategory::Tech ),
84 FUNCDATA( Oct2Dec, UNIQUE, STDPAR, 1, FDCategory::Tech ),
85 FUNCDATA( Oct2Hex, UNIQUE, INTPAR, 2, FDCategory::Tech ),
86 FUNCDATA( Dec2Bin, UNIQUE, INTPAR, 2, FDCategory::Tech ),
87 FUNCDATA( Dec2Hex, UNIQUE, INTPAR, 2, FDCategory::Tech ),
88 FUNCDATA( Dec2Oct, UNIQUE, INTPAR, 2, FDCategory::Tech ),
89 FUNCDATA( Hex2Bin, UNIQUE, INTPAR, 2, FDCategory::Tech ),
90 FUNCDATA( Hex2Dec, UNIQUE, STDPAR, 1, FDCategory::Tech ),
91 FUNCDATA( Hex2Oct, UNIQUE, INTPAR, 2, FDCategory::Tech ),
92 FUNCDATA( Delta, UNIQUE, INTPAR, 2, FDCategory::Tech ),
93 FUNCDATA( Erf, UNIQUE, INTPAR, 2, FDCategory::Tech ),
94 FUNCDATA( Erfc, UNIQUE, STDPAR, 1, FDCategory::Tech ),
95 FUNCDATA( Gestep, UNIQUE, INTPAR, 2, FDCategory::Tech ),
96 FUNCDATA( Factdouble, UNIQUE, STDPAR, 1, FDCategory::Tech ),
97 FUNCDATA( Imabs, UNIQUE, STDPAR, 1, FDCategory::Tech ),
98 FUNCDATA( Imaginary, UNIQUE, STDPAR, 1, FDCategory::Tech ),
99 FUNCDATA( Impower, UNIQUE, STDPAR, 2, FDCategory::Tech ),
100 FUNCDATA( Imargument, UNIQUE, STDPAR, 1, FDCategory::Tech ),
101 FUNCDATA( Imcos, UNIQUE, STDPAR, 1, FDCategory::Tech ),
102 FUNCDATA( Imdiv, UNIQUE, STDPAR, 2, FDCategory::Tech ),
103 FUNCDATA( Imexp, UNIQUE, STDPAR, 1, FDCategory::Tech ),
104 FUNCDATA( Imconjugate, UNIQUE, STDPAR, 1, FDCategory::Tech ),
105 FUNCDATA( Imln, UNIQUE, STDPAR, 1, FDCategory::Tech ),
106 FUNCDATA( Imlog10, UNIQUE, STDPAR, 1, FDCategory::Tech ),
107 FUNCDATA( Imlog2, UNIQUE, STDPAR, 1, FDCategory::Tech ),
108 FUNCDATA( Improduct, UNIQUE, INTPAR, 2, FDCategory::Tech ),
109 FUNCDATA( Imreal, UNIQUE, STDPAR, 1, FDCategory::Tech ),
110 FUNCDATA( Imsin, UNIQUE, STDPAR, 1, FDCategory::Tech ),
111 FUNCDATA( Imsub, UNIQUE, STDPAR, 2, FDCategory::Tech ),
112 FUNCDATA( Imsqrt, UNIQUE, STDPAR, 1, FDCategory::Tech ),
113 FUNCDATA( Imsum, UNIQUE, INTPAR, 1, FDCategory::Tech ),
114 FUNCDATA( Imtan, UNIQUE, STDPAR, 1, FDCategory::Tech ),
115 FUNCDATA( Imsec, UNIQUE, STDPAR, 1, FDCategory::Tech ),
116 FUNCDATA( Imcsc, UNIQUE, STDPAR, 1, FDCategory::Tech ),
117 FUNCDATA( Imcot, UNIQUE, STDPAR, 1, FDCategory::Tech ),
118 FUNCDATA( Imsinh, UNIQUE, STDPAR, 1, FDCategory::Tech ),
119 FUNCDATA( Imcosh, UNIQUE, STDPAR, 1, FDCategory::Tech ),
120 FUNCDATA( Imsech, UNIQUE, STDPAR, 1, FDCategory::Tech ),
121 FUNCDATA( Imcsch, UNIQUE, STDPAR, 1, FDCategory::Tech ),
122 FUNCDATA( Complex, UNIQUE, STDPAR, 3, FDCategory::Tech ),
123 FUNCDATA( Convert, UNIQUE, STDPAR, 3, FDCategory::Tech ),
124 FUNCDATA( Amordegrc, UNIQUE, INTPAR, 7, FDCategory::Finance ),
125 FUNCDATA( Amorlinc, UNIQUE, INTPAR, 7, FDCategory::Finance ),
126 FUNCDATA( Accrint, UNIQUE, INTPAR, 7, FDCategory::Finance ),
127 FUNCDATA( Accrintm, UNIQUE, INTPAR, 5, FDCategory::Finance ),
128 FUNCDATA( Received, UNIQUE, INTPAR, 5, FDCategory::Finance ),
129 FUNCDATA( Disc, UNIQUE, INTPAR, 5, FDCategory::Finance ),
130 FUNCDATA( Duration, UNIQUE, INTPAR, 6, FDCategory::Finance ),
131 FUNCDATA( Effect, DOUBLE, STDPAR, 2, FDCategory::Finance ),
132 FUNCDATA( Cumprinc, DOUBLE, STDPAR, 6, FDCategory::Finance ),
133 FUNCDATA( Cumipmt, DOUBLE, STDPAR, 6, FDCategory::Finance ),
134 FUNCDATA( Price, UNIQUE, INTPAR, 7, FDCategory::Finance ),
135 FUNCDATA( Pricedisc, UNIQUE, INTPAR, 5, FDCategory::Finance ),
136 FUNCDATA( Pricemat, UNIQUE, INTPAR, 6, FDCategory::Finance ),
137 FUNCDATA( Mduration, UNIQUE, INTPAR, 6, FDCategory::Finance ),
138 FUNCDATA( Nominal, DOUBLE, STDPAR, 2, FDCategory::Finance ),
139 FUNCDATA( Dollarfr, UNIQUE, STDPAR, 2, FDCategory::Finance ),
140 FUNCDATA( Dollarde, UNIQUE, STDPAR, 2, FDCategory::Finance ),
141 FUNCDATA( Yield, UNIQUE, INTPAR, 7, FDCategory::Finance ),
142 FUNCDATA( Yielddisc, UNIQUE, INTPAR, 5, FDCategory::Finance ),
143 FUNCDATA( Yieldmat, UNIQUE, INTPAR, 6, FDCategory::Finance ),
144 FUNCDATA( Tbilleq, UNIQUE, INTPAR, 3, FDCategory::Finance ),
145 FUNCDATA( Tbillprice, UNIQUE, INTPAR, 3, FDCategory::Finance ),
146 FUNCDATA( Tbillyield, UNIQUE, INTPAR, 3, FDCategory::Finance ),
147 FUNCDATA( Oddfprice, UNIQUE, INTPAR, 9, FDCategory::Finance ),
148 FUNCDATA( Oddfyield, UNIQUE, INTPAR, 9, FDCategory::Finance ),
149 FUNCDATA( Oddlprice, UNIQUE, INTPAR, 8, FDCategory::Finance ),
150 FUNCDATA( Oddlyield, UNIQUE, INTPAR, 8, FDCategory::Finance ),
151 FUNCDATA( Xirr, UNIQUE, INTPAR, 3, FDCategory::Finance ),
152 FUNCDATA( Xnpv, UNIQUE, STDPAR, 3, FDCategory::Finance ),
153 FUNCDATA( Intrate, UNIQUE, INTPAR, 5, FDCategory::Finance ),
154 FUNCDATA( Coupncd, UNIQUE, INTPAR, 4, FDCategory::Finance ),
155 FUNCDATA( Coupdays, UNIQUE, INTPAR, 4, FDCategory::Finance ),
156 FUNCDATA( Coupdaysnc, UNIQUE, INTPAR, 4, FDCategory::Finance ),
157 FUNCDATA( Coupdaybs, UNIQUE, INTPAR, 4, FDCategory::Finance ),
158 FUNCDATA( Couppcd, UNIQUE, INTPAR, 4, FDCategory::Finance ),
159 FUNCDATA( Coupnum, UNIQUE, INTPAR, 4, FDCategory::Finance ),
160 FUNCDATA( Fvschedule, UNIQUE, STDPAR, 2, FDCategory::Finance )
162 #undef FUNCDATA
164 namespace sca::analysis {
166 sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
168 if( (nMonth == 2) && IsLeapYear( nYear ) )
169 return 29;
170 static const sal_uInt16 aDaysInMonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
171 return aDaysInMonth[ nMonth ];
176 * Convert a date to a count of days starting from 01/01/0001
178 * The internal representation of a Date used in this Addin
179 * is the number of days between 01/01/0001 and the date
180 * this function converts a Day , Month, Year representation
181 * to this internal Date value.
185 sal_Int32 DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
187 sal_Int32 nDays = (static_cast<sal_Int32>(nYear)-1) * 365;
188 nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);
190 for( sal_uInt16 i = 1; i < nMonth; i++ )
191 nDays += DaysInMonth(i,nYear);
192 nDays += nDay;
194 return nDays;
199 * Convert a count of days starting from 01/01/0001 to a date
201 * The internal representation of a Date used in this Addin
202 * is the number of days between 01/01/0001 and the date
203 * this function converts this internal Date value
204 * to a Day , Month, Year representation of a Date.
208 void DaysToDate( sal_Int32 nDays, sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear )
210 if( nDays < 0 )
211 throw lang::IllegalArgumentException();
213 sal_Int32 nTempDays;
214 sal_Int32 i = 0;
215 bool bCalc;
219 nTempDays = nDays;
220 rYear = static_cast<sal_uInt16>((nTempDays / 365) - i);
221 nTempDays -= (static_cast<sal_Int32>(rYear) -1) * 365;
222 nTempDays -= (( rYear -1) / 4) - (( rYear -1) / 100) + ((rYear -1) / 400);
223 bCalc = false;
224 if ( nTempDays < 1 )
226 i++;
227 bCalc = true;
229 else
231 if ( nTempDays > 365 )
233 if ( (nTempDays != 366) || !IsLeapYear( rYear ) )
235 i--;
236 bCalc = true;
241 while ( bCalc );
243 rMonth = 1;
244 while ( nTempDays > DaysInMonth( rMonth, rYear ) )
246 nTempDays -= DaysInMonth( rMonth, rYear );
247 rMonth++;
249 rDay = static_cast<sal_uInt16>(nTempDays);
254 * Get the null date used by the spreadsheet document
256 * The internal representation of a Date used in this Addin
257 * is the number of days between 01/01/0001 and the date
258 * this function returns this internal Date value for the document null date
262 sal_Int32 GetNullDate( const uno::Reference< beans::XPropertySet >& xOpt )
264 if( xOpt.is() )
268 uno::Any aAny = xOpt->getPropertyValue( u"NullDate"_ustr );
269 util::Date aDate;
270 if( aAny >>= aDate )
271 return DateToDays( aDate.Day, aDate.Month, aDate.Year );
273 catch( uno::Exception& )
278 // no null date available -> no calculations possible
279 throw uno::RuntimeException();
283 sal_Int32 GetDiffDate360(
284 sal_uInt16 nDay1, sal_uInt16 nMonth1, sal_uInt16 nYear1, bool bLeapYear1,
285 sal_uInt16 nDay2, sal_uInt16 nMonth2, sal_uInt16 nYear2,
286 bool bUSAMethod )
288 if( nDay1 == 31 )
289 nDay1--;
290 else if( bUSAMethod && ( nMonth1 == 2 && ( nDay1 == 29 || ( nDay1 == 28 && !bLeapYear1 ) ) ) )
291 nDay1 = 30;
293 if( nDay2 == 31 )
295 if( bUSAMethod && nDay1 != 30 )
297 nDay2 = 1;
298 if( nMonth2 == 12 )
300 nYear2++;
301 nMonth2 = 1;
303 else
304 nMonth2++;
306 else
307 nDay2 = 30;
310 return nDay2 + nMonth2 * 30 + nYear2 * 360 - nDay1 - nMonth1 * 30 - nYear1 * 360;
314 sal_Int32 GetDiffDate360( sal_Int32 nNullDate, sal_Int32 nDate1, sal_Int32 nDate2, bool bUSAMethod )
316 nDate1 += nNullDate;
317 nDate2 += nNullDate;
319 sal_uInt16 nDay1, nMonth1, nYear1, nDay2, nMonth2, nYear2;
321 DaysToDate( nDate1, nDay1, nMonth1, nYear1 );
322 DaysToDate( nDate2, nDay2, nMonth2, nYear2 );
324 return GetDiffDate360( nDay1, nMonth1, nYear1, IsLeapYear( nYear1 ), nDay2, nMonth2, nYear2, bUSAMethod );
328 sal_Int32 GetDaysInYears( sal_uInt16 nYear1, sal_uInt16 nYear2 )
330 sal_uInt16 nLeaps = 0;
331 for( sal_uInt16 n = nYear1 ; n <= nYear2 ; n++ )
333 if( IsLeapYear( n ) )
334 nLeaps++;
337 sal_uInt32 nSum = 1;
338 nSum += nYear2;
339 nSum -= nYear1;
340 nSum *= 365;
341 nSum += nLeaps;
343 return nSum;
347 sal_Int32 GetDiffDate( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode,
348 sal_Int32* pOptDaysIn1stYear )
350 bool bNeg = nStartDate > nEndDate;
352 if( bNeg )
353 std::swap( nStartDate, nEndDate );
355 sal_Int32 nRet;
357 switch( nMode )
359 case 0: // 0=USA (NASD) 30/360
360 case 4: // 4=Europe 30/360
362 sal_uInt16 nD1, nM1, nY1, nD2, nM2, nY2;
364 nStartDate += nNullDate;
365 nEndDate += nNullDate;
367 DaysToDate( nStartDate, nD1, nM1, nY1 );
368 DaysToDate( nEndDate, nD2, nM2, nY2 );
370 bool bLeap = IsLeapYear( nY1 );
371 sal_Int32 nDays, nMonths;
373 nMonths = nM2 - nM1;
374 nDays = nD2 - nD1;
376 nMonths += ( nY2 - nY1 ) * 12;
378 nRet = nMonths * 30 + nDays;
379 if( nMode == 0 && nM1 == 2 && nM2 != 2 && nY1 == nY2 )
380 nRet -= bLeap? 1 : 2;
382 if( pOptDaysIn1stYear )
383 *pOptDaysIn1stYear = 360;
385 break;
386 case 1: // 1=exact/exact
387 if( pOptDaysIn1stYear )
389 sal_uInt16 nD, nM, nY;
391 DaysToDate( nStartDate + nNullDate, nD, nM, nY );
393 *pOptDaysIn1stYear = IsLeapYear( nY )? 366 : 365;
395 nRet = nEndDate - nStartDate;
396 break;
397 case 2: // 2=exact/360
398 nRet = nEndDate - nStartDate;
399 if( pOptDaysIn1stYear )
400 *pOptDaysIn1stYear = 360;
401 break;
402 case 3: //3=exact/365
403 nRet = nEndDate - nStartDate;
404 if( pOptDaysIn1stYear )
405 *pOptDaysIn1stYear = 365;
406 break;
407 default:
408 throw lang::IllegalArgumentException();
411 return bNeg? -nRet : nRet;
415 double GetYearDiff( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode )
417 sal_Int32 nDays1stYear;
418 sal_Int32 nTotalDays = GetDiffDate( nNullDate, nStartDate, nEndDate, nMode, &nDays1stYear );
420 return double( nTotalDays ) / double( nDays1stYear );
424 sal_Int32 GetDaysInYear( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nMode )
426 switch( nMode )
428 case 0: // 0=USA (NASD) 30/360
429 case 2: // 2=exact/360
430 case 4: // 4=Europe 30/360
431 return 360;
432 case 1: // 1=exact/exact
434 sal_uInt16 nD, nM, nY;
435 nDate += nNullDate;
436 DaysToDate( nDate, nD, nM, nY );
437 return IsLeapYear( nY )? 366 : 365;
439 case 3: //3=exact/365
440 return 365;
441 default:
442 throw lang::IllegalArgumentException();
447 // tdf69569 making code compliant with change request for ODFF1.2 par 4.11.7.7
449 * Function GetYearFrac implements YEARFRAC as defined in:
450 * Open Document Format for Office Applications version 1.2 Part 2, par. 6.10.24
451 * The calculations are defined in:
452 * Open Document Format for Office Applications version 1.2 Part 2, par. 4.11.7
454 double GetYearFrac( sal_Int32 nNullDate, sal_Int32 nStartDate, sal_Int32 nEndDate, sal_Int32 nMode )
456 if( nStartDate == nEndDate )
457 return 0.0; // nothing to do...
459 if( nStartDate > nEndDate )
460 std::swap( nStartDate, nEndDate );
462 sal_Int32 nDate1 = nStartDate + nNullDate;
463 sal_Int32 nDate2 = nEndDate + nNullDate;
465 sal_uInt16 nDay1, nDay2;
466 sal_uInt16 nMonth1, nMonth2;
467 sal_uInt16 nYear1, nYear2;
469 DaysToDate( nDate1, nDay1, nMonth1, nYear1 );
470 DaysToDate( nDate2, nDay2, nMonth2, nYear2 );
472 // calculate days between nDate1 and nDate2
473 sal_Int32 nDayDiff;
474 switch( nMode )
476 case 0: // 0=USA (NASD) 30/360
477 if ( nDay1 == 31 )
479 nDay1--;
481 if ( nDay1 == 30 && nDay2 == 31 )
483 nDay2--;
485 else
487 if ( nMonth1 == 2 && nDay1 == ( IsLeapYear( nYear1 ) ? 29 : 28 ) )
489 nDay1 = 30;
490 if ( nMonth2 == 2 && nDay2 == ( IsLeapYear( nYear2 ) ? 29 : 28 ) )
492 nDay2 = 30;
496 nDayDiff = ( nYear2 - nYear1 ) * 360 + ( nMonth2 - nMonth1 ) * 30 + ( nDay2 - nDay1 );
497 break;
498 case 1: // 1=exact/exact
499 case 2: // 2=exact/360
500 case 3: // 3=exact/365
501 nDayDiff = nDate2 - nDate1;
502 break;
503 case 4: // 4=Europe 30/360
504 if ( nDay1 == 31 )
506 nDay1--;
508 if ( nDay2 == 31 )
510 nDay2--;
512 nDayDiff = ( nYear2 - nYear1 ) * 360 + ( nMonth2 - nMonth1 ) * 30 + ( nDay2 - nDay1 );
513 break;
514 default:
515 throw lang::IllegalArgumentException();
518 //calculate days in year
519 double nDaysInYear;
520 switch( nMode )
522 case 0: // 0=USA (NASD) 30/360
523 case 2: // 2=exact/360
524 case 4: // 4=Europe 30/360
525 nDaysInYear = 360;
526 break;
527 case 1: // 1=exact/exact
529 const bool isYearDifferent = ( nYear1 != nYear2 );
530 // ODFv1.2 part 2 section 4.11.7.7.7
531 if ( isYearDifferent &&
532 ( ( nYear2 != nYear1 + 1 ) ||
533 ( nMonth1 < nMonth2 ) ||
534 ( nMonth1 == nMonth2 && nDay1 < nDay2 ) ) )
536 // return average of days in year between nDate1 and nDate2, inclusive
537 sal_Int32 nDayCount = 0;
538 for ( sal_uInt16 i = nYear1; i <= nYear2; i++ )
539 nDayCount += ( IsLeapYear( i ) ? 366 : 365 );
541 nDaysInYear = static_cast<double>(nDayCount) / static_cast<double>( nYear2 - nYear1 + 1 );
543 else
545 // as a consequence, !isYearDifferent or
546 // nYear2 == nYear + 1 and (nMonth1 > nMonth2 or
547 // (nMonth1 == nMonth2 and nDay1 >= nDay2))
548 assert( ( !isYearDifferent ||
549 ( nYear1 + 1 == nYear2 &&
550 ( nMonth1 > nMonth2 ||
551 ( nMonth1 == nMonth2 || nDay1 >= nDay2 ) ) ) ) );
553 // ODFv1.2 part 2 section 4.11.7.7.8 (CHANGE REQUEST PENDING, see tdf6959)
554 if ( !isYearDifferent && IsLeapYear( nYear1 ) )
556 nDaysInYear = 366;
558 else
560 // ODFv1.2 part 2 section 4.11.7.7.9/10 (CHANGE REQUEST PENDING, see tdf69569)
561 // we need to determine whether there is a 29 February
562 // between nDate1 (inclusive) and nDate2 (inclusive)
563 // the case of nYear1 == nYear2 is adequately tested in previous test
564 if( isYearDifferent &&
565 ( ( IsLeapYear( nYear1 ) &&
566 ( ( nMonth1 < 2 ) || ( ( nMonth1 == 2 ) && ( nDay1 <= 29 ) ) ) ) ||
567 ( IsLeapYear( nYear2 ) &&
568 ( nMonth2 > 2 || ( ( nMonth2 == 2 ) && ( nDay2 == 29 ) ) ) ) ) )
570 nDaysInYear = 366;
572 else
574 nDaysInYear = 365;
579 break;
580 case 3: // 3=exact/365
581 nDaysInYear = 365;
582 break;
583 // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
584 default:
585 throw lang::IllegalArgumentException();
588 return double( nDayDiff ) / nDaysInYear;
591 double BinomialCoefficient( double n, double k )
593 // This method is a copy of BinomKoeff()
594 // found in sc/source/core/tool/interpr3.cxx
596 double nVal = 0.0;
597 k = ::rtl::math::approxFloor(k);
598 if (n < k)
599 nVal = 0.0;
600 else if (k == 0.0)
601 nVal = 1.0;
602 else
604 nVal = n/k;
605 n--;
606 k--;
607 while (k > 0.0)
609 nVal *= n/k;
610 k--;
611 n--;
614 return nVal;
617 double GetGcd( double f1, double f2 )
619 double f = fmod( f1, f2 );
620 while( f > 0.0 )
622 f1 = f2;
623 f2 = f;
624 f = fmod( f1, f2 );
627 return f2;
631 double ConvertToDec( const OUString& aStr, sal_uInt16 nBase, sal_uInt16 nCharLim )
633 if ( nBase < 2 || nBase > 36 )
634 throw lang::IllegalArgumentException();
636 sal_uInt32 nStrLen = aStr.getLength();
637 if( nStrLen > nCharLim )
638 throw lang::IllegalArgumentException();
639 else if( !nStrLen )
640 return 0.0;
642 double fVal = 0.0;
644 const sal_Unicode* p = aStr.getStr();
646 sal_uInt16 nFirstDig = 0;
647 bool bFirstDig = true;
648 double fBase = nBase;
650 while ( *p )
652 sal_uInt16 n;
654 if( '0' <= *p && *p <= '9' )
655 n = *p - '0';
656 else if( 'A' <= *p && *p <= 'Z' )
657 n = 10 + ( *p - 'A' );
658 else if ( 'a' <= *p && *p <= 'z' )
659 n = 10 + ( *p - 'a' );
660 else
661 n = nBase;
663 if( n >= nBase )
664 throw lang::IllegalArgumentException(); // illegal char!
666 if( bFirstDig )
668 bFirstDig = false;
669 nFirstDig = n;
671 fVal = fVal * fBase + double( n );
673 p++;
677 if( nStrLen == nCharLim && !bFirstDig && (nFirstDig >= nBase / 2) )
678 { // handling negative values
679 fVal = ( pow( double( nBase ), double( nCharLim ) ) - fVal ); // complement
680 fVal *= -1.0;
683 return fVal;
687 static char GetMaxChar( sal_uInt16 nBase )
689 const char* const c = "--123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
690 return c[ nBase ];
694 OUString ConvertFromDec( double fNum, double fMin, double fMax, sal_uInt16 nBase,
695 sal_Int32 nPlaces, sal_Int32 nMaxPlaces, bool bUsePlaces )
697 fNum = ::rtl::math::approxFloor( fNum );
698 fMin = ::rtl::math::approxFloor( fMin );
699 fMax = ::rtl::math::approxFloor( fMax );
701 if( fNum < fMin || fNum > fMax || ( bUsePlaces && ( nPlaces <= 0 || nPlaces > nMaxPlaces ) ) )
702 throw lang::IllegalArgumentException();
704 sal_Int64 nNum = static_cast< sal_Int64 >( fNum );
705 bool bNeg = nNum < 0;
706 if( bNeg )
707 nNum = sal_Int64( pow( double( nBase ), double( nMaxPlaces ) ) ) + nNum;
709 OUString aRet(OUString::number(nNum, nBase).toAsciiUpperCase());
712 if( bUsePlaces )
714 sal_Int32 nLen = aRet.getLength();
715 if( !bNeg && nLen > nPlaces )
717 throw lang::IllegalArgumentException();
719 else if( ( bNeg && nLen < nMaxPlaces ) || ( !bNeg && nLen < nPlaces ) )
721 sal_Int32 nLeft = nPlaces - nLen;
722 std::unique_ptr<char[]> p( new char[ nLeft + 1 ] );
723 memset( p.get(), bNeg ? GetMaxChar( nBase ) : '0', nLeft );
724 p[ nLeft ] = 0x00;
725 aRet = OUString( p.get(), nLeft, RTL_TEXTENCODING_MS_1252 ) + aRet;
729 return aRet;
732 double Erf( double x )
734 return std::erf(x);
737 double Erfc( double x )
739 return std::erfc(x);
742 static bool IsNum( sal_Unicode c )
744 return c >= '0' && c <= '9';
748 static bool IsComma( sal_Unicode c )
750 return c == '.' || c == ',';
754 static bool IsExpStart( sal_Unicode c )
756 return c == 'e' || c == 'E';
760 static bool IsImagUnit( sal_Unicode c )
762 return c == 'i' || c == 'j';
766 static sal_uInt16 GetVal( sal_Unicode c )
768 return sal_uInt16( c - '0' );
772 bool ParseDouble( const sal_Unicode*& rp, double& rRet )
774 double fInt = 0.0;
775 double fFrac = 0.0;
776 double fMult = 0.1; // multiplier to multiply digits with, when adding fractional ones
777 sal_Int32 nExp = 0;
778 sal_Int32 nMaxExp = 307;
779 sal_uInt16 nDigCnt = 18; // max. number of digits to read in, rest doesn't matter
781 enum State { S_End, S_Sign, S_IntStart, S_Int, S_IgnoreIntDigs, S_Frac, S_IgnoreFracDigs, S_ExpSign, S_Exp };
783 State eS = S_Sign;
785 bool bNegNum = false;
786 bool bNegExp = false;
788 const sal_Unicode* p = rp;
789 sal_Unicode c;
791 while( eS != S_End )
793 c = *p;
794 switch( eS )
796 case S_Sign:
797 if( IsNum( c ) )
799 fInt = GetVal( c );
800 nDigCnt--;
801 eS = S_Int;
803 else if( c == '-' )
805 bNegNum = true;
806 eS = S_IntStart;
808 else if( c == '+' )
809 eS = S_IntStart;
810 else if( IsComma( c ) )
811 eS = S_Frac;
812 else
813 return false;
814 break;
815 case S_IntStart:
816 if( IsNum( c ) )
818 fInt = GetVal( c );
819 nDigCnt--;
820 eS = S_Int;
822 else if( IsComma( c ) )
823 eS = S_Frac;
824 else if( IsImagUnit( c ) )
826 rRet = 0.0;
827 return true;
829 else
830 return false;
831 break;
832 case S_Int:
833 if( IsNum( c ) )
835 fInt *= 10.0;
836 fInt += double( GetVal( c ) );
837 nDigCnt--;
838 if( !nDigCnt )
839 eS = S_IgnoreIntDigs;
841 else if( IsComma( c ) )
842 eS = S_Frac;
843 else if( IsExpStart( c ) )
844 eS = S_ExpSign;
845 else
846 eS = S_End;
847 break;
848 case S_IgnoreIntDigs:
849 if( IsNum( c ) )
850 nExp++; // just multiply num with 10... ;-)
851 else if( IsComma( c ) )
852 eS = S_Frac;
853 else if( IsExpStart( c ) )
854 eS = S_ExpSign;
855 else
856 eS = S_End;
857 break;
858 case S_Frac:
859 if( IsNum( c ) )
861 fFrac += double( GetVal( c ) ) * fMult;
862 nDigCnt--;
863 if( nDigCnt )
864 fMult *= 0.1;
865 else
866 eS = S_IgnoreFracDigs;
868 else if( IsExpStart( c ) )
869 eS = S_ExpSign;
870 else
871 eS = S_End;
872 break;
873 case S_IgnoreFracDigs:
874 if( IsExpStart( c ) )
875 eS = S_ExpSign;
876 else if( !IsNum( c ) )
877 eS = S_End;
878 break;
879 case S_ExpSign:
880 if( IsNum( c ) )
882 nExp = GetVal( c );
883 eS = S_Exp;
885 else if( c == '-' )
887 bNegExp = true;
888 eS = S_Exp;
890 else if( c != '+' )
891 eS = S_End;
892 break;
893 case S_Exp:
894 if( IsNum( c ) )
896 nExp *= 10;
897 nExp += GetVal( c );
898 if( nExp > nMaxExp )
899 return false;
901 else
902 eS = S_End;
903 break;
904 // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
905 case S_End:
906 break;
909 p++;
912 p--; // set pointer back to last
913 rp = p;
915 fInt += fFrac;
917 if (fInt != 0.0) // exact check; log10(0.0) may entail a pole error
919 sal_Int32 nLog10 = sal_Int32( log10( fInt ) );
921 if( bNegExp )
922 nExp = -nExp;
924 if( nLog10 + nExp > nMaxExp )
925 return false;
927 fInt = ::rtl::math::pow10Exp( fInt, nExp );
930 if( bNegNum )
931 fInt = -fInt;
933 rRet = fInt;
935 return true;
939 OUString GetString( double f, bool bLeadingSign, sal_uInt16 nMaxDig )
941 const int nBuff = 256;
942 char aBuff[ nBuff + 1 ];
943 const char* pFormStr = bLeadingSign? "%+.*g" : "%.*g";
944 int nLen = snprintf( aBuff, nBuff, pFormStr, int( nMaxDig ), f );
945 // you never know which underlying implementation you get ...
946 aBuff[nBuff] = 0;
947 if ( nLen < 0 || nLen > nBuff )
948 nLen = strlen( aBuff );
950 OUString aRet( aBuff, nLen, RTL_TEXTENCODING_MS_1252 );
952 return aRet;
956 double GetAmordegrc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer,
957 double fRestVal, double fPer, double fRate, sal_Int32 nBase )
959 sal_uInt32 nPer = sal_uInt32( fPer );
960 double fUsePer = 1.0 / fRate;
961 double fAmorCoeff;
963 if( fUsePer < 3.0 )
964 fAmorCoeff = 1.0;
965 else if( fUsePer < 5.0 )
966 fAmorCoeff = 1.5;
967 else if( fUsePer <= 6.0 )
968 fAmorCoeff = 2.0;
969 else
970 fAmorCoeff = 2.5;
972 fRate *= fAmorCoeff;
973 double fNRate = ::rtl::math::round( GetYearFrac( nNullDate, nDate, nFirstPer, nBase ) * fRate * fCost );
974 fCost -= fNRate;
975 double fRest = fCost - fRestVal; // aboriginal cost - residual value - sum of all write-downs
977 for( sal_uInt32 n = 0 ; n < nPer ; n++ )
979 fNRate = ::rtl::math::round( fRate * fCost );
980 fRest -= fNRate;
982 if( fRest < 0.0 )
984 switch( nPer - n )
986 case 0:
987 case 1:
988 return ::rtl::math::round( fCost * 0.5 );
989 default:
990 return 0.0;
994 fCost -= fNRate;
997 return fNRate;
1001 double GetAmorlinc( sal_Int32 nNullDate, double fCost, sal_Int32 nDate, sal_Int32 nFirstPer,
1002 double fRestVal, double fPer, double fRate, sal_Int32 nBase )
1004 sal_uInt32 nPer = sal_uInt32( fPer );
1005 double fOneRate = fCost * fRate;
1006 double fCostDelta = fCost - fRestVal;
1007 double f0Rate = GetYearFrac( nNullDate, nDate, nFirstPer, nBase ) * fRate * fCost;
1008 sal_uInt32 nNumOfFullPeriods = sal_uInt32( ( fCost - fRestVal - f0Rate) / fOneRate );
1010 double fResult = 0.0;
1011 if( nPer == 0 )
1012 fResult = f0Rate;
1013 else if( nPer <= nNumOfFullPeriods )
1014 fResult = fOneRate;
1015 else if( nPer == nNumOfFullPeriods + 1 )
1016 fResult = fCostDelta - fOneRate * nNumOfFullPeriods - f0Rate;
1018 if ( fResult > 0.0 )
1019 return fResult;
1020 else
1021 return 0.0;
1025 double GetDuration( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup,
1026 double fYield, sal_Int32 nFreq, sal_Int32 nBase )
1028 double fYearfrac = GetYearFrac( nNullDate, nSettle, nMat, nBase );
1029 double fNumOfCoups = GetCoupnum( nNullDate, nSettle, nMat, nFreq, nBase );
1030 double fDur = 0.0;
1031 const double f100 = 100.0;
1032 fCoup *= f100 / double( nFreq ); // fCoup is used as cash flow
1033 fYield /= nFreq;
1034 fYield += 1.0;
1036 double nDiff = fYearfrac * nFreq - fNumOfCoups;
1038 double t;
1040 for( t = 1.0 ; t < fNumOfCoups ; t++ )
1041 fDur += ( t + nDiff ) * fCoup / pow( fYield, t + nDiff );
1043 fDur += ( fNumOfCoups + nDiff ) * ( fCoup + f100 ) / pow( fYield, fNumOfCoups + nDiff );
1045 double p = 0.0;
1046 for( t = 1.0 ; t < fNumOfCoups ; t++ )
1047 p += fCoup / pow( fYield, t + nDiff );
1049 p += ( fCoup + f100 ) / pow( fYield, fNumOfCoups + nDiff );
1051 fDur /= p;
1052 fDur /= double( nFreq );
1054 return fDur;
1058 double GetYieldmat( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nIssue,
1059 double fRate, double fPrice, sal_Int32 nBase )
1061 double fIssMat = GetYearFrac( nNullDate, nIssue, nMat, nBase );
1062 double fIssSet = GetYearFrac( nNullDate, nIssue, nSettle, nBase );
1063 double fSetMat = GetYearFrac( nNullDate, nSettle, nMat, nBase );
1065 double y = 1.0 + fIssMat * fRate;
1066 y /= fPrice / 100.0 + fIssSet * fRate;
1067 y--;
1068 y /= fSetMat;
1070 return y;
1074 double GetOddfprice( sal_Int32 /*nNullDate*/, sal_Int32 /*nSettle*/, sal_Int32 /*nMat*/, sal_Int32 /*nIssue*/,
1075 sal_Int32 /*nFirstCoup*/, double /*fRate*/, double /*fYield*/, double /*fRedemp*/, sal_Int32 /*nFreq*/,
1076 sal_Int32 /*nBase*/ )
1078 // If you change this to not unconditionally throw, the
1079 // SAL_WNOUNREACHABLE_CODE_PUSH/POP around the caller in
1080 // financial.cxx can be removed.
1081 throw uno::RuntimeException();
1085 double getYield_( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fCoup, double fPrice,
1086 double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1088 double fRate = fCoup;
1089 double fPriceN = 0.0;
1090 double fYield1 = 0.0;
1091 double fYield2 = 1.0;
1092 double fPrice1 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield1, fRedemp, nFreq, nBase );
1093 double fPrice2 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield2, fRedemp, nFreq, nBase );
1094 double fYieldN = ( fYield2 - fYield1 ) * 0.5;
1096 for( sal_uInt32 nIter = 0 ; nIter < 100 && !rtl::math::approxEqual(fPriceN, fPrice) ; nIter++ )
1098 fPriceN = getPrice_( nNullDate, nSettle, nMat, fRate, fYieldN, fRedemp, nFreq, nBase );
1100 if( rtl::math::approxEqual(fPrice, fPrice1) )
1101 return fYield1;
1102 else if( rtl::math::approxEqual(fPrice, fPrice2) )
1103 return fYield2;
1104 else if( rtl::math::approxEqual(fPrice, fPriceN) )
1105 return fYieldN;
1106 else if( fPrice < fPrice2 )
1108 fYield2 *= 2.0;
1109 fPrice2 = getPrice_( nNullDate, nSettle, nMat, fRate, fYield2, fRedemp, nFreq, nBase );
1111 fYieldN = ( fYield2 - fYield1 ) * 0.5;
1113 else
1115 if( fPrice < fPriceN )
1117 fYield1 = fYieldN;
1118 fPrice1 = fPriceN;
1120 else
1122 fYield2 = fYieldN;
1123 fPrice2 = fPriceN;
1126 fYieldN = fYield2 - ( fYield2 - fYield1 ) * ( ( fPrice - fPrice2 ) / ( fPrice1 - fPrice2 ) );
1130 if( fabs( fPrice - fPriceN ) > fPrice / 100.0 )
1131 throw lang::IllegalArgumentException(); // result not precise enough
1133 return fYieldN;
1137 double getPrice_( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, double fRate, double fYield,
1138 double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1140 double fFreq = nFreq;
1142 double fE = GetCoupdays( nNullDate, nSettle, nMat, nFreq, nBase );
1143 double fDSC_E = GetCoupdaysnc( nNullDate, nSettle, nMat, nFreq, nBase ) / fE;
1144 double fN = GetCoupnum( nNullDate, nSettle, nMat, nFreq, nBase );
1145 double fA = GetCoupdaybs( nNullDate, nSettle, nMat, nFreq, nBase );
1147 double fRet = fRedemp / ( pow( 1.0 + fYield / fFreq, fN - 1.0 + fDSC_E ) );
1148 fRet -= 100.0 * fRate / fFreq * fA / fE;
1150 double fT1 = 100.0 * fRate / fFreq;
1151 double fT2 = 1.0 + fYield / fFreq;
1153 for( double fK = 0.0 ; fK < fN ; fK++ )
1154 fRet += fT1 / pow( fT2, fK + fDSC_E );
1156 return fRet;
1160 double GetOddfyield( sal_Int32 /*nNullDate*/, sal_Int32 /*nSettle*/, sal_Int32 /*nMat*/, sal_Int32 /*nIssue*/,
1161 sal_Int32 /*nFirstCoup*/, double /*fRate*/, double /*fPrice*/, double /*fRedemp*/, sal_Int32 /*nFreq*/,
1162 sal_Int32 /*nBase*/ )
1164 // If you change this to not unconditionally throw, the
1165 // SAL_WNOUNREACHABLE_CODE_PUSH/POP around the caller in
1166 // financial.cxx can be removed.
1167 throw uno::RuntimeException();
1171 double GetOddlprice( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastCoup,
1172 double fRate, double fYield, double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1174 double fFreq = double( nFreq );
1175 double fDCi = GetYearFrac( nNullDate, nLastCoup, nMat, nBase ) * fFreq;
1176 double fDSCi = GetYearFrac( nNullDate, nSettle, nMat, nBase ) * fFreq;
1177 double fAi = GetYearFrac( nNullDate, nLastCoup, nSettle, nBase ) * fFreq;
1179 double p = fRedemp + fDCi * 100.0 * fRate / fFreq;
1180 p /= fDSCi * fYield / fFreq + 1.0;
1181 p -= fAi * 100.0 * fRate / fFreq;
1183 return p;
1187 double GetOddlyield( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nLastCoup,
1188 double fRate, double fPrice, double fRedemp, sal_Int32 nFreq, sal_Int32 nBase )
1190 double fFreq = double( nFreq );
1191 double fDCi = GetYearFrac( nNullDate, nLastCoup, nMat, nBase ) * fFreq;
1192 double fDSCi = GetYearFrac( nNullDate, nSettle, nMat, nBase ) * fFreq;
1193 double fAi = GetYearFrac( nNullDate, nLastCoup, nSettle, nBase ) * fFreq;
1195 double y = fRedemp + fDCi * 100.0 * fRate / fFreq;
1196 y /= fPrice + fAi * 100.0 * fRate / fFreq;
1197 y--;
1198 y *= fFreq / fDSCi;
1200 return y;
1204 double GetPmt( double fRate, double fNper, double fPv, double fFv, sal_Int32 nPayType )
1206 double fPmt;
1207 if( fRate == 0.0 )
1208 fPmt = ( fPv + fFv ) / fNper;
1209 else
1211 double fTerm = pow( 1.0 + fRate, fNper );
1212 if( nPayType > 0 )
1213 fPmt = ( fFv * fRate / ( fTerm - 1.0 ) + fPv * fRate / ( 1.0 - 1.0 / fTerm ) ) / ( 1.0 + fRate );
1214 else
1215 fPmt = fFv * fRate / ( fTerm - 1.0 ) + fPv * fRate / ( 1.0 - 1.0 / fTerm );
1218 return -fPmt;
1222 double GetFv( double fRate, double fNper, double fPmt, double fPv, sal_Int32 nPayType )
1224 double fFv;
1225 if( fRate == 0.0 )
1226 fFv = fPv + fPmt * fNper;
1227 else
1229 double fTerm = pow( 1.0 + fRate, fNper );
1230 if( nPayType > 0 )
1231 fFv = fPv * fTerm + fPmt * ( 1.0 + fRate ) * ( fTerm - 1.0 ) / fRate;
1232 else
1233 fFv = fPv * fTerm + fPmt * ( fTerm - 1.0 ) / fRate;
1236 return -fFv;
1239 // financial functions COUP***
1241 // COUPPCD: find last coupon date before settlement (can be equal to settlement)
1242 /// @throws css::lang::IllegalArgumentException
1243 static void lcl_GetCouppcd( ScaDate& rDate, const ScaDate& rSettle, const ScaDate& rMat, sal_Int32 nFreq )
1245 rDate = rMat;
1246 rDate.setYear( rSettle.getYear() );
1247 if( rDate < rSettle )
1248 rDate.addYears( 1 );
1249 while( rDate > rSettle )
1250 rDate.addMonths( -12 / nFreq );
1253 double GetCouppcd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1255 if( nSettle >= nMat || isFreqInvalid(nFreq) )
1256 throw lang::IllegalArgumentException();
1258 ScaDate aDate;
1259 lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1260 return aDate.getDate( nNullDate );
1263 // COUPNCD: find first coupon date after settlement (is never equal to settlement)
1264 /// @throws css::lang::IllegalArgumentException
1265 static void lcl_GetCoupncd( ScaDate& rDate, const ScaDate& rSettle, const ScaDate& rMat, sal_Int32 nFreq )
1267 rDate = rMat;
1268 rDate.setYear( rSettle.getYear() );
1269 if( rDate > rSettle )
1270 rDate.addYears( -1 );
1271 while( rDate <= rSettle )
1272 rDate.addMonths( 12 / nFreq );
1275 double GetCoupncd( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1277 if( nSettle >= nMat || isFreqInvalid(nFreq) )
1278 throw lang::IllegalArgumentException();
1280 ScaDate aDate;
1281 lcl_GetCoupncd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1282 return aDate.getDate( nNullDate );
1285 // COUPDAYBS: get day count: coupon date before settlement <-> settlement
1286 double GetCoupdaybs( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1288 if( nSettle >= nMat || isFreqInvalid(nFreq) )
1289 throw lang::IllegalArgumentException();
1291 ScaDate aSettle( nNullDate, nSettle, nBase );
1292 ScaDate aDate;
1293 lcl_GetCouppcd( aDate, aSettle, ScaDate( nNullDate, nMat, nBase ), nFreq );
1294 return ScaDate::getDiff( aDate, aSettle );
1297 // COUPDAYSNC: get day count: settlement <-> coupon date after settlement
1298 double GetCoupdaysnc( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1300 if( nSettle >= nMat || isFreqInvalid(nFreq) )
1301 throw lang::IllegalArgumentException();
1303 if( (nBase != 0) && (nBase != 4) )
1305 ScaDate aSettle( nNullDate, nSettle, nBase );
1306 ScaDate aDate;
1307 lcl_GetCoupncd( aDate, aSettle, ScaDate( nNullDate, nMat, nBase ), nFreq );
1308 return ScaDate::getDiff( aSettle, aDate );
1310 return GetCoupdays( nNullDate, nSettle, nMat, nFreq, nBase ) - GetCoupdaybs( nNullDate, nSettle, nMat, nFreq, nBase );
1313 // COUPDAYS: get day count: coupon date before settlement <-> coupon date after settlement
1314 double GetCoupdays( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1316 if( nSettle >= nMat || isFreqInvalid(nFreq) )
1317 throw lang::IllegalArgumentException();
1319 if( nBase == 1 )
1321 ScaDate aDate;
1322 lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), ScaDate( nNullDate, nMat, nBase ), nFreq );
1323 ScaDate aNextDate( aDate );
1324 aNextDate.addMonths( 12 / nFreq );
1325 return ScaDate::getDiff( aDate, aNextDate );
1327 return static_cast< double >( GetDaysInYear( 0, 0, nBase ) ) / nFreq;
1330 // COUPNUM: get count of coupon dates
1331 double GetCoupnum( sal_Int32 nNullDate, sal_Int32 nSettle, sal_Int32 nMat, sal_Int32 nFreq, sal_Int32 nBase )
1333 if( nSettle >= nMat || isFreqInvalid(nFreq) )
1334 throw lang::IllegalArgumentException();
1336 ScaDate aMat( nNullDate, nMat, nBase );
1337 ScaDate aDate;
1338 lcl_GetCouppcd( aDate, ScaDate( nNullDate, nSettle, nBase ), aMat, nFreq );
1339 sal_uInt16 nMonths = (aMat.getYear() - aDate.getYear()) * 12 + aMat.getMonth() - aDate.getMonth();
1340 return static_cast< double >( nMonths * nFreq / 12 );
1343 FuncData::FuncData(const FuncDataBase& r) :
1344 aIntName( OUString::createFromAscii( r.pIntName ) ),
1345 pUINameID( r.pUINameID ),
1346 pDescrID( r.pDescrID ),
1347 bDouble( r.bDouble ),
1348 bWithOpt( r.bWithOpt ),
1349 nParam( r.nNumOfParams ),
1350 eCat( r.eCat )
1352 if (r.pSuffix)
1353 aSuffix = OUString::createFromAscii(r.pSuffix);
1355 aCompList.resize(2);
1356 aCompList[0] = OUString(r.pCompListID[0], strlen(r.pCompListID[0]), RTL_TEXTENCODING_UTF8);
1357 aCompList[1] = OUString(r.pCompListID[1], strlen(r.pCompListID[1]), RTL_TEXTENCODING_UTF8);
1360 sal_uInt16 FuncData::GetStrIndex( sal_uInt16 nParamNum ) const
1362 if( !bWithOpt )
1363 nParamNum++;
1365 if( nParamNum > nParam )
1366 return nParam * 2;
1367 else
1368 return nParamNum * 2;
1371 void InitFuncDataList(FuncDataList& rList)
1373 for(const auto & rFuncData : pFuncDatas)
1374 rList.emplace_back(rFuncData);
1377 SortedIndividualInt32List::SortedIndividualInt32List()
1382 SortedIndividualInt32List::~SortedIndividualInt32List()
1387 void SortedIndividualInt32List::Insert( sal_Int32 nDay )
1389 sal_uInt32 nIndex = Count();
1390 while( nIndex )
1392 nIndex--;
1393 sal_Int32 nRef = Get( nIndex );
1394 if( nDay == nRef )
1395 return;
1396 else if( nDay > nRef )
1398 maVector.insert( maVector.begin() + nIndex + 1, nDay );
1399 return;
1402 maVector.insert( maVector.begin(), nDay );
1406 void SortedIndividualInt32List::Insert( sal_Int32 nDay, sal_Int32 nNullDate, bool bInsertOnWeekend )
1408 if( !nDay )
1409 return;
1411 nDay += nNullDate;
1412 if( bInsertOnWeekend || (GetDayOfWeek( nDay ) < 5) )
1413 Insert( nDay );
1417 void SortedIndividualInt32List::Insert(
1418 double fDay, sal_Int32 nNullDate, bool bInsertOnWeekend )
1420 if( (fDay < -2147483648.0) || (fDay > 2147483649.0) )
1421 throw lang::IllegalArgumentException();
1422 Insert( static_cast< sal_Int32 >( fDay ), nNullDate, bInsertOnWeekend );
1426 bool SortedIndividualInt32List::Find( sal_Int32 nVal ) const
1428 sal_uInt32 nE = Count();
1430 if( !nE || nVal < Get( 0 ) || nVal > Get( nE - 1 ) )
1431 return false;
1433 // linear search
1435 for( sal_uInt32 n = 0 ; n < nE ; n++ )
1437 sal_Int32 nRef = Get( n );
1439 if( nRef == nVal )
1440 return true;
1441 else if( nRef > nVal )
1442 return false;
1444 return false;
1448 void SortedIndividualInt32List::InsertHolidayList(
1449 const ScaAnyConverter& rAnyConv,
1450 const uno::Any& rHolAny,
1451 sal_Int32 nNullDate,
1452 bool bInsertOnWeekend )
1454 double fDay;
1455 if( rAnyConv.getDouble( fDay, rHolAny ) )
1456 Insert( fDay, nNullDate, bInsertOnWeekend );
1460 void SortedIndividualInt32List::InsertHolidayList(
1461 ScaAnyConverter& rAnyConv,
1462 const uno::Reference< beans::XPropertySet >& xOptions,
1463 const uno::Any& rHolAny,
1464 sal_Int32 nNullDate )
1466 rAnyConv.init( xOptions );
1467 if( rHolAny.getValueTypeClass() == uno::TypeClass_SEQUENCE )
1469 uno::Sequence< uno::Sequence< uno::Any > > aAnySeq;
1470 if( !(rHolAny >>= aAnySeq) )
1471 throw lang::IllegalArgumentException();
1473 for (const uno::Sequence<uno::Any>& rSubSeq : aAnySeq)
1475 for( const uno::Any& rAny : rSubSeq )
1476 InsertHolidayList( rAnyConv, rAny, nNullDate, false/*bInsertOnWeekend*/ );
1479 else
1480 InsertHolidayList( rAnyConv, rHolAny, nNullDate, false/*bInsertOnWeekend*/ );
1484 void ScaDoubleList::Append(
1485 const uno::Sequence< uno::Sequence< double > >& rValueSeq )
1487 for( const uno::Sequence< double >& rSubSeq : rValueSeq )
1489 for( const double fValue : rSubSeq )
1490 Append( fValue );
1495 void ScaDoubleList::Append(
1496 const uno::Sequence< uno::Sequence< sal_Int32 > >& rValueSeq )
1498 for( const uno::Sequence< sal_Int32 >& rSubSeq : rValueSeq )
1500 for( const sal_Int32 nValue : rSubSeq )
1501 Append( nValue );
1505 void ScaDoubleList::Append(
1506 const ScaAnyConverter& rAnyConv,
1507 const uno::Any& rAny,
1508 bool bIgnoreEmpty )
1510 if( auto s = o3tl::tryAccess<
1511 css::uno::Sequence<css::uno::Sequence<css::uno::Any>>>(rAny) )
1512 Append( rAnyConv, *s, bIgnoreEmpty );
1513 else
1515 double fValue;
1516 if( rAnyConv.getDouble( fValue, rAny ) )
1517 Append( fValue );
1518 else if( !bIgnoreEmpty )
1519 Append( 0.0 );
1524 void ScaDoubleList::Append(
1525 const ScaAnyConverter& rAnyConv,
1526 const uno::Sequence< uno::Any >& rAnySeq,
1527 bool bIgnoreEmpty )
1529 for( const uno::Any& rAny : rAnySeq )
1530 Append( rAnyConv, rAny, bIgnoreEmpty );
1534 void ScaDoubleList::Append(
1535 const ScaAnyConverter& rAnyConv,
1536 const uno::Sequence< uno::Sequence< uno::Any > >& rAnySeq,
1537 bool bIgnoreEmpty )
1539 for( const uno::Sequence< uno::Any >& rArray : rAnySeq )
1540 Append( rAnyConv, rArray, bIgnoreEmpty );
1543 void ScaDoubleList::Append(
1544 ScaAnyConverter& rAnyConv,
1545 const uno::Reference< beans::XPropertySet >& xOpt,
1546 const uno::Sequence< uno::Any >& rAnySeq )
1548 rAnyConv.init( xOpt );
1549 Append( rAnyConv, rAnySeq, true/*bIgnoreEmpty*/ );
1553 bool ScaDoubleList::CheckInsert( double ) const
1555 return true;
1559 bool ScaDoubleListGT0::CheckInsert( double fValue ) const
1561 if( fValue < 0.0 )
1562 throw lang::IllegalArgumentException();
1563 return fValue > 0.0;
1567 bool ScaDoubleListGE0::CheckInsert( double fValue ) const
1569 if( fValue < 0.0 )
1570 throw lang::IllegalArgumentException();
1571 return true;
1575 Complex::Complex( const OUString& rStr )
1577 if( !ParseString( rStr, *this ) )
1578 throw lang::IllegalArgumentException();
1582 inline bool Complex::IsImagUnit( sal_Unicode c )
1584 return c == 'i' || c == 'j';
1587 bool Complex::ParseString( const OUString& rStr, Complex& rCompl )
1589 rCompl.c = '\0'; // do not force a symbol, if only real part present
1591 const sal_Unicode* pStr = rStr.getStr();
1593 if( IsImagUnit( *pStr ) && rStr.getLength() == 1)
1595 rCompl.c = *pStr;
1596 rCompl.num = std::complex(0.0, 1.0);
1597 return true;
1600 double f;
1602 if( !ParseDouble( pStr, f ) )
1603 return false;
1605 switch( *pStr )
1607 case '-': // imag part follows
1608 case '+':
1610 double r = f;
1611 if( IsImagUnit( pStr[ 1 ] ) )
1613 rCompl.c = pStr[ 1 ];
1614 if( pStr[ 2 ] == 0 )
1616 rCompl.num = std::complex(f, ( *pStr == '+' )? 1.0 : -1.0);
1617 return true;
1620 else if( ParseDouble( pStr, f ) && IsImagUnit( *pStr ) )
1622 rCompl.c = *pStr;
1623 pStr++;
1624 if( *pStr == 0 )
1626 rCompl.num = std::complex(r, f);
1627 return true;
1631 break;
1632 case 'j':
1633 case 'i':
1634 rCompl.c = *pStr;
1635 pStr++;
1636 if( *pStr == 0 )
1638 rCompl.num = std::complex(0.0, f);
1639 return true;
1641 break;
1642 case 0: // only real-part
1643 rCompl.num = std::complex(f, 0.0);
1644 return true;
1647 return false;
1650 OUString Complex::GetString() const
1652 finiteOrThrow(num.real());
1653 finiteOrThrow(num.imag());
1654 OUStringBuffer aRet;
1656 bool bHasImag = num.imag() != 0.0;
1657 bool bHasReal = !bHasImag || (num.real() != 0.0);
1659 if( bHasReal )
1660 aRet.append(::GetString( num.real(), false ));
1661 if( bHasImag )
1663 if( num.imag() == 1.0 )
1665 if( bHasReal )
1666 aRet.append('+');
1668 else if( num.imag() == -1.0 )
1669 aRet.append('-');
1670 else
1671 aRet.append(::GetString( num.imag(), bHasReal ));
1672 aRet.append((c != 'j') ? 'i' : 'j');
1675 return aRet.makeStringAndClear();
1679 double Complex::Arg() const
1681 // Note: there are differing opinions on whether arg(0) should be 0 or undefined, we are treating it as undefined
1682 if( num.real() == 0.0 && num.imag() == 0.0 )
1683 throw lang::IllegalArgumentException();
1684 return std::arg(num);
1688 void Complex::Power( double fPower )
1690 if( num.real() == 0.0 && num.imag() == 0.0 && fPower <= 0 )
1691 throw lang::IllegalArgumentException();
1692 num = std::pow(num, fPower);
1696 void Complex::Sqrt()
1698 num = std::sqrt(num);
1702 void Complex::Sin()
1704 if( !::rtl::math::isValidArcArg( num.real() ) )
1705 throw lang::IllegalArgumentException();
1706 num = std::sin(num);
1710 void Complex::Cos()
1712 if( !::rtl::math::isValidArcArg( num.real() ) )
1713 throw lang::IllegalArgumentException();
1714 num = std::cos(num);
1718 void Complex::Div( const Complex& z )
1720 if( z.num.real() == 0 && z.num.imag() == 0 )
1721 throw lang::IllegalArgumentException();
1722 num = num / z.num;
1726 void Complex::Exp()
1728 num = std::exp(num);
1731 void Complex::Ln()
1733 num = std::log(num);
1737 void Complex::Log10()
1739 num = std::log10(num);
1743 void Complex::Log2()
1745 Ln();
1746 Mult( M_LOG2E );
1750 void Complex::Tan()
1752 // using 2.0 * num.real/imag as a precaution because a) this is what our previous implementation did and
1753 // b) the std::complex implementation may use cos(2x) etc, see the comment in isValidArcArg for details
1754 if ( ( num.imag() && !::rtl::math::isValidArcArg( 2.0 * num.real() ) )
1755 || ( !num.imag() && !::rtl::math::isValidArcArg( num.real() ) ) )
1756 throw lang::IllegalArgumentException();
1757 num = std::tan(num);
1761 void Complex::Sec()
1763 Cos();
1764 num = 1.0 / num;
1768 void Complex::Csc()
1770 Sin();
1771 num = 1.0 / num;
1775 void Complex::Cot()
1778 Tan();
1779 num = 1.0 / num;
1783 void Complex::Sinh()
1785 if( !::rtl::math::isValidArcArg( num.imag() ) )
1786 throw lang::IllegalArgumentException();
1787 num = std::sinh(num);
1791 void Complex::Cosh()
1793 if( !::rtl::math::isValidArcArg( num.imag() ) )
1794 throw lang::IllegalArgumentException();
1795 num = std::cosh(num);
1799 void Complex::Sech()
1801 Cosh();
1802 num = 1.0 / num;
1806 void Complex::Csch()
1808 Sinh();
1809 num = 1.0 / num;
1813 ComplexList::~ComplexList()
1818 void ComplexList::Append( const uno::Sequence< uno::Sequence< OUString > >& r )
1820 for( const uno::Sequence< OUString >& rList : r )
1822 for( const OUString& rStr : rList )
1824 if( !rStr.isEmpty() )
1825 Append( Complex( rStr ) );
1831 void ComplexList::Append( const uno::Sequence< uno::Any >& aMultPars )
1833 for( const uno::Any& r : aMultPars )
1835 switch( r.getValueTypeClass() )
1837 case uno::TypeClass_VOID: break;
1838 case uno::TypeClass_STRING:
1840 auto pStr = o3tl::forceAccess<OUString>(r);
1842 if( !pStr->isEmpty() )
1843 Append( Complex( *pStr ) );
1845 break;
1846 case uno::TypeClass_DOUBLE:
1847 Append( Complex( *o3tl::forceAccess<double>(r), 0.0 ) );
1848 break;
1849 case uno::TypeClass_SEQUENCE:
1851 uno::Sequence< uno::Sequence< uno::Any > > aValArr;
1852 if( !(r >>= aValArr) )
1853 throw lang::IllegalArgumentException();
1855 for (const uno::Sequence<uno::Any>& rArr : aValArr)
1856 Append( rArr );
1858 break;
1859 default:
1860 throw lang::IllegalArgumentException();
1865 ConvertData::ConvertData(std::u16string_view sUnitName, double fC, ConvertDataClass e, bool bPrefSupport)
1866 : fConst(fC)
1867 , aName(sUnitName)
1868 , eClass(e)
1869 , bPrefixSupport(bPrefSupport)
1871 assert(!aName.empty());
1874 ConvertData::~ConvertData() = default;
1876 sal_Int16 ConvertData::GetMatchingLevel( const OUString& rRef ) const
1878 OUString aStr = rRef;
1879 if (sal_Int32 nIndex = rRef.lastIndexOf('^'); nIndex > 0 && nIndex == (rRef.getLength() - 2))
1880 aStr = aStr.replaceAt(nIndex, 1, "");
1881 if( aName == aStr )
1882 return 0;
1883 if (std::u16string_view prefix; bPrefixSupport && aStr.endsWith(aName, &prefix))
1885 if (prefix.size() == 1 || prefix == u"da")
1887 sal_Int16 n;
1888 switch (prefix[0])
1890 case 'y': n = -24; break; // yocto
1891 case 'z': n = -21; break; // zepto
1892 case 'a': n = -18; break;
1893 case 'f': n = -15; break;
1894 case 'p': n = -12; break;
1895 case 'n': n = -9; break;
1896 case 'u': n = -6; break;
1897 case 'm': n = -3; break;
1898 case 'c': n = -2; break;
1899 case 'd':
1900 if (prefix.size() == 1)
1901 n = -1; // deci
1902 else
1903 n = 1; // deca
1904 break;
1905 case 'e': n = 1; break;
1906 case 'h': n = 2; break;
1907 case 'k': n = 3; break;
1908 case 'M': n = 6; break;
1909 case 'G': n = 9; break;
1910 case 'T': n = 12; break;
1911 case 'P': n = 15; break;
1912 case 'E': n = 18; break;
1913 case 'Z': n = 21; break; // zetta
1914 case 'Y': n = 24; break; // yotta
1915 default: return INV_MATCHLEV;
1918 // We could weed some nonsense out, ODFF doesn't say so though.
1919 #if 0
1920 if (n < 0 && Class() == CDC_Information)
1921 return INV_MATCHLEV; // milli-bits doesn't make sense
1922 #endif
1924 //! <HACK> "cm3" is not 10^-2 m^3 but 10^-6 m^3 !!! ------------------
1925 if (aStr.endsWith("2"))
1926 n *= 2;
1927 else if (aStr.endsWith("3"))
1928 n *= 3;
1929 //! </HACK> -------------------------------------------------------------------
1931 return n;
1933 else if (prefix.size() == 2 && prefix[1] == 'i' && Class() == CDC_Information)
1935 switch (prefix[0])
1937 case 'k': return 10;
1938 case 'M': return 20;
1939 case 'G': return 30;
1940 case 'T': return 40;
1941 case 'P': return 50;
1942 case 'E': return 60;
1943 case 'Z': return 70;
1944 case 'Y': return 80;
1945 default: return INV_MATCHLEV;
1949 return INV_MATCHLEV;
1953 double ConvertData::Convert(
1954 double f, const ConvertData& r, sal_Int16 nLevFrom, sal_Int16 nLevTo ) const
1956 assert(Class() == r.Class());
1958 f *= r.fConst / fConst;
1960 if (Class() == CDC_Information)
1962 bool bBinFromLev = (nLevFrom > 0 && (nLevFrom % 10) == 0);
1963 bool bBinToLev = (nLevTo > 0 && (nLevTo % 10) == 0);
1964 if (bBinFromLev || bBinToLev)
1966 if (bBinFromLev && bBinToLev)
1968 nLevFrom -= nLevTo;
1969 if (nLevFrom)
1970 f *= pow(2.0, nLevFrom);
1972 else if (bBinFromLev)
1973 f *= pow(2.0, nLevFrom) / pow(10.0, nLevTo);
1974 else
1975 f *= pow(10.0, nLevFrom) / pow(2.0, nLevTo);
1976 return f;
1980 nLevFrom -= nLevTo; // effective level
1981 if( nLevFrom )
1982 f = ::rtl::math::pow10Exp( f, nLevFrom );
1984 return f;
1988 ConvertDataLinear::~ConvertDataLinear() = default;
1990 double ConvertDataLinear::Convert(
1991 double f, const ConvertData& r, sal_Int16 nLevFrom, sal_Int16 nLevTo ) const
1993 assert(Class() == r.Class());
1994 assert(dynamic_cast<const ConvertDataLinear*>(&r));
1995 return static_cast<const ConvertDataLinear&>(r).ConvertFromBase( ConvertToBase( f, nLevFrom ), nLevTo );
1999 double ConvertDataLinear::ConvertToBase( double f, sal_Int16 n ) const
2001 if( n )
2002 f = ::rtl::math::pow10Exp( f, n );
2004 f /= fConst;
2005 f -= fOffs;
2007 return f;
2011 double ConvertDataLinear::ConvertFromBase( double f, sal_Int16 n ) const
2013 f += fOffs;
2014 f *= fConst;
2016 if( n )
2017 f = ::rtl::math::pow10Exp( f, -n );
2019 return f;
2023 ConvertDataList::ConvertDataList()
2025 #define NEWD(str,unit,cl) maVector.push_back(std::make_unique<ConvertData>(u"" str,unit,cl))
2026 #define NEWDP(str,unit,cl) maVector.push_back(std::make_unique<ConvertData>(u"" str,unit,cl,true))
2027 #define NEWL(str,unit,offs,cl) maVector.push_back(std::make_unique<ConvertDataLinear>(u"" str,unit,offs,cl))
2028 #define NEWLP(str,unit,offs,cl) maVector.push_back(std::make_unique<ConvertDataLinear>(u"" str,unit,offs,cl,true))
2030 const size_t expected_size = 146;
2031 maVector.reserve(expected_size);
2033 // *** are extra and not standard Excel Analysis Addin!
2035 // MASS: 1 Gram is...
2036 NEWDP( "g", 1.0000000000000000E00, CDC_Mass ); // Gram
2037 NEWD( "sg", 6.8522050005347800E-05, CDC_Mass ); // Pieces
2038 NEWD( "lbm", 2.2046229146913400E-03, CDC_Mass ); // Pound (commercial weight)
2039 NEWDP( "u", 6.0221370000000000E23, CDC_Mass ); // U (atomic mass)
2040 NEWD( "ozm", 3.5273971800362700E-02, CDC_Mass ); // Ounce (commercial weight)
2041 NEWD( "stone", 1.574730e-04, CDC_Mass ); // *** Stone
2042 NEWD( "ton", 1.102311e-06, CDC_Mass ); // *** Ton
2043 NEWD( "grain", 1.543236E01, CDC_Mass ); // *** Grain
2044 NEWD( "pweight", 7.054792E-01, CDC_Mass ); // *** Pennyweight
2045 NEWD( "hweight", 1.968413E-05, CDC_Mass ); // *** Hundredweight
2046 NEWD( "shweight", 2.204623E-05, CDC_Mass ); // *** Shorthundredweight
2047 NEWD( "brton", 9.842065E-07, CDC_Mass ); // *** Gross Registered Ton
2048 NEWD( "cwt", 2.2046226218487758E-05, CDC_Mass ); // U.S. (short) hundredweight
2049 NEWD( "shweight", 2.2046226218487758E-05, CDC_Mass ); // U.S. (short) hundredweight also
2050 NEWD( "uk_cwt", 1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight
2051 NEWD( "lcwt", 1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight also
2052 NEWD( "hweight", 1.9684130552221213E-05, CDC_Mass ); // Imperial hundredweight also
2053 NEWD( "uk_ton", 9.8420652761106063E-07, CDC_Mass ); // Imperial ton
2054 NEWD( "LTON", 9.8420652761106063E-07, CDC_Mass ); // Imperial ton also
2056 // LENGTH: 1 Meter is...
2057 NEWDP( "m", 1.0000000000000000E00, CDC_Length ); // Meter
2058 NEWD( "mi", 6.2137119223733397E-04, CDC_Length ); // Britsh Mile 6,21371192237333969617434184363e-4
2059 NEWD( "Nmi", 5.3995680345572354E-04, CDC_Length ); // Nautical Mile 5,39956803455723542116630669546e-4
2060 NEWD( "in", 3.9370078740157480E01, CDC_Length ); // Inch 39,37007874015748031496062992126
2061 NEWD( "ft", 3.2808398950131234E00, CDC_Length ); // Foot 3,2808398950131233595800524934383
2062 NEWD( "yd", 1.0936132983377078E00, CDC_Length ); // Yard 1,0936132983377077865266841644794
2063 NEWDP( "ang", 1.0000000000000000E10, CDC_Length ); // Angstrom
2064 NEWD( "Pica", 2.8346456692913386E03, CDC_Length ); // Pica Point (1/72 Inch) 2834,6456692913385826771653543307
2065 NEWD( "picapt", 2.8346456692913386E03, CDC_Length ); // Pica Point (1/72 Inch) 2834,6456692913385826771653543307
2066 NEWD( "pica", 2.36220472441E02, CDC_Length ); // pica (1/6 Inch)
2067 NEWD( "ell", 8.748906E-01, CDC_Length ); // *** Ell
2068 NEWDP( "parsec", 3.240779E-17, CDC_Length ); // *** Parsec
2069 NEWDP( "pc", 3.240779E-17, CDC_Length ); // *** Parsec also
2070 NEWDP( "lightyear", 1.0570234557732930E-16, CDC_Length ); // *** Light Year
2071 NEWDP( "ly", 1.0570234557732930E-16, CDC_Length ); // *** Light Year also
2072 NEWD( "survey_mi", 6.2136994949494949E-04, CDC_Length ); // U.S. survey mile
2074 // TIME: 1 Second is...
2075 NEWD( "yr", 3.1688087814028950E-08, CDC_Time ); // Year
2076 NEWD( "day", 1.1574074074074074E-05, CDC_Time ); // Day
2077 NEWD( "d", 1.1574074074074074E-05, CDC_Time ); // Day also
2078 NEWD( "hr", 2.7777777777777778E-04, CDC_Time ); // Hour
2079 NEWD( "mn", 1.6666666666666667E-02, CDC_Time ); // Minute
2080 NEWD( "min", 1.6666666666666667E-02, CDC_Time ); // Minute also
2081 NEWDP( "sec", 1.0000000000000000E00, CDC_Time ); // Second
2082 NEWDP( "s", 1.0000000000000000E00, CDC_Time ); // Second also
2084 // PRESSURE: 1 Pascal is...
2085 NEWDP( "Pa", 1.0000000000000000E00, CDC_Pressure ); // Pascal
2086 NEWDP( "atm", 9.8692329999819300E-06, CDC_Pressure ); // Atmosphere
2087 NEWDP( "at", 9.8692329999819300E-06, CDC_Pressure ); // Atmosphere also
2088 NEWDP( "mmHg", 7.5006170799862700E-03, CDC_Pressure ); // mm Hg (Mercury)
2089 NEWD( "Torr", 7.5006380000000000E-03, CDC_Pressure ); // *** Torr
2090 NEWD( "psi", 1.4503770000000000E-04, CDC_Pressure ); // *** Psi
2092 // FORCE: 1 Newton is...
2093 NEWDP( "N", 1.0000000000000000E00, CDC_Force ); // Newton
2094 NEWDP( "dyn", 1.0000000000000000E05, CDC_Force ); // Dyn
2095 NEWDP( "dy", 1.0000000000000000E05, CDC_Force ); // Dyn also
2096 NEWD( "lbf", 2.24808923655339E-01, CDC_Force ); // Pound-Force
2097 NEWDP( "pond", 1.019716E02, CDC_Force ); // *** Pond
2099 // ENERGY: 1 Joule is...
2100 NEWDP( "J", 1.0000000000000000E00, CDC_Energy ); // Joule
2101 NEWDP( "e", 1.0000000000000000E07, CDC_Energy ); // Erg -> https://en.wikipedia.org/wiki/Erg
2102 NEWDP( "c", 2.3900624947346700E-01, CDC_Energy ); // Thermodynamical Calorie
2103 NEWDP( "cal", 2.3884619064201700E-01, CDC_Energy ); // Calorie
2104 NEWDP( "eV", 6.2414570000000000E18, CDC_Energy ); // Electronvolt
2105 NEWDP( "ev", 6.2414570000000000E18, CDC_Energy ); // Electronvolt also
2106 NEWD( "HPh", 3.7250611111111111E-07, CDC_Energy ); // Horsepower Hours
2107 NEWD( "hh", 3.7250611111111111E-07, CDC_Energy ); // Horsepower Hours also
2108 NEWDP( "Wh", 2.7777777777777778E-04, CDC_Energy ); // Watt Hours
2109 NEWDP( "wh", 2.7777777777777778E-04, CDC_Energy ); // Watt Hours also
2110 NEWD( "flb", 2.37304222192651E01, CDC_Energy ); // Foot Pound
2111 NEWD( "BTU", 9.4781506734901500E-04, CDC_Energy ); // British Thermal Unit
2112 NEWD( "btu", 9.4781506734901500E-04, CDC_Energy ); // British Thermal Unit also
2114 // POWER: 1 Watt is...
2115 NEWDP( "W", 1.0000000000000000E00, CDC_Power ); // Watt
2116 NEWDP( "w", 1.0000000000000000E00, CDC_Power ); // Watt also
2117 NEWD( "HP", 1.341022E-03, CDC_Power ); // Horsepower
2118 NEWD( "h", 1.341022E-03, CDC_Power ); // Horsepower also
2119 NEWD( "PS", 1.359622E-03, CDC_Power ); // *** German Pferdestaerke
2121 // MAGNETISM: 1 Tesla is...
2122 NEWDP( "T", 1.0000000000000000E00, CDC_Magnetism ); // Tesla
2123 NEWDP( "ga", 1.0000000000000000E04, CDC_Magnetism ); // Gauss
2125 // TEMPERATURE: 1 Kelvin is...
2126 NEWL( "C", 1.0000000000000000E00, -2.7315000000000000E02, CDC_Temperature ); // Celsius
2127 NEWL( "cel", 1.0000000000000000E00, -2.7315000000000000E02, CDC_Temperature ); // Celsius also
2128 NEWL( "F", 1.8000000000000000E00, -2.5537222222222222E02, CDC_Temperature ); // Fahrenheit
2129 NEWL( "fah", 1.8000000000000000E00, -2.5537222222222222E02, CDC_Temperature ); // Fahrenheit also
2130 NEWLP( "K", 1.0000000000000000E00, +0.0000000000000000E00, CDC_Temperature ); // Kelvin
2131 NEWLP( "kel", 1.0000000000000000E00, +0.0000000000000000E00, CDC_Temperature ); // Kelvin also
2132 NEWL( "Reau", 8.0000000000000000E-01, -2.7315000000000000E02, CDC_Temperature ); // *** Reaumur
2133 NEWL( "Rank", 1.8000000000000000E00, +0.0000000000000000E00, CDC_Temperature ); // *** Rankine
2135 // VOLUME: 1 Liter is...
2136 NEWD( "tsp", 2.0288413621105798E02, CDC_Volume ); // US teaspoon 1/768 gallon
2137 NEWD( "tbs", 6.7628045403685994E01, CDC_Volume ); // US tablespoon 1/256 gallon
2138 NEWD( "oz", 3.3814022701842997E01, CDC_Volume ); // Ounce Liquid 1/128 gallon
2139 NEWD( "cup", 4.2267528377303746E00, CDC_Volume ); // Cup 1/16 gallon
2140 NEWD( "pt", 2.1133764188651873E00, CDC_Volume ); // US Pint 1/8 gallon
2141 NEWD( "us_pt", 2.1133764188651873E00, CDC_Volume ); // US Pint also
2142 NEWD( "uk_pt", 1.7597539863927023E00, CDC_Volume ); // UK Pint 1/8 imperial gallon
2143 NEWD( "qt", 1.0566882094325937E00, CDC_Volume ); // Quart 1/4 gallon
2144 NEWD( "gal", 2.6417205235814842E-01, CDC_Volume ); // Gallon 1/3.785411784
2145 NEWDP( "l", 1.0000000000000000E00, CDC_Volume ); // Liter
2146 NEWDP( "L", 1.0000000000000000E00, CDC_Volume ); // Liter also
2147 NEWDP( "lt", 1.0000000000000000E00, CDC_Volume ); // Liter also
2148 NEWDP( "m3", 1.0000000000000000E-03, CDC_Volume ); // *** Cubic Meter
2149 NEWD( "mi3", 2.3991275857892772E-13, CDC_Volume ); // *** Cubic Britsh Mile
2150 NEWD( "Nmi3", 1.5742621468581148E-13, CDC_Volume ); // *** Cubic Nautical Mile
2151 NEWD( "in3", 6.1023744094732284E01, CDC_Volume ); // *** Cubic Inch
2152 NEWD( "ft3", 3.5314666721488590E-02, CDC_Volume ); // *** Cubic Foot
2153 NEWD( "yd3", 1.3079506193143922E-03, CDC_Volume ); // *** Cubic Yard
2154 NEWDP( "ang3", 1.0000000000000000E27, CDC_Volume ); // *** Cubic Angstrom
2155 NEWD( "Pica3", 2.2776990435870636E07, CDC_Volume ); // *** Cubic Pica Point (1/72 inch)
2156 NEWD( "picapt3", 2.2776990435870636E07, CDC_Volume ); // *** Cubic Pica Point (1/72 inch)
2157 NEWD( "pica3", 1.31811287245E04, CDC_Volume ); // *** Cubic Pica (1/6 inch)
2158 NEWD( "barrel", 6.2898107704321051E-03, CDC_Volume ); // *** Barrel (=42gal)
2159 NEWD( "bushel", 2.837759E-02, CDC_Volume ); // *** Bushel
2160 NEWD( "regton", 3.531467E-04, CDC_Volume ); // *** Register ton
2161 NEWD( "GRT", 3.531467E-04, CDC_Volume ); // *** Register ton also
2162 NEWD( "Schooner", 2.3529411764705882E00, CDC_Volume ); // *** austr. Schooner
2163 NEWD( "Middy", 3.5087719298245614E00, CDC_Volume ); // *** austr. Middy
2164 NEWD( "Glass", 5.0000000000000000E00, CDC_Volume ); // *** austr. Glass
2165 NEWD( "Sixpack", 0.5, CDC_Volume ); // ***
2166 NEWD( "Humpen", 2.0, CDC_Volume ); // ***
2167 NEWD( "ly3", 1.1810108125623799E-51, CDC_Volume ); // *** Cubic light-year
2168 NEWD( "MTON", 1.4125866688595436E00, CDC_Volume ); // *** Measurement ton
2169 NEWD( "tspm", 2.0000000000000000E02, CDC_Volume ); // *** Modern teaspoon
2170 NEWD( "uk_gal", 2.1996924829908779E-01, CDC_Volume ); // U.K. / Imperial gallon 1/4.54609
2171 NEWD( "uk_qt", 8.7987699319635115E-01, CDC_Volume ); // U.K. / Imperial quart 1/4 imperial gallon
2173 // 1 Square Meter is...
2174 NEWDP( "m2", 1.0000000000000000E00, CDC_Area ); // *** Square Meter
2175 NEWD( "mi2", 3.8610215854244585E-07, CDC_Area ); // *** Square Britsh Mile
2176 NEWD( "Nmi2", 2.9155334959812286E-07, CDC_Area ); // *** Square Nautical Mile
2177 NEWD( "in2", 1.5500031000062000E03, CDC_Area ); // *** Square Inch
2178 NEWD( "ft2", 1.0763910416709722E01, CDC_Area ); // *** Square Foot
2179 NEWD( "yd2", 1.1959900463010803E00, CDC_Area ); // *** Square Yard
2180 NEWDP( "ang2", 1.0000000000000000E20, CDC_Area ); // *** Square Angstrom
2181 NEWD( "Pica2", 8.0352160704321409E06, CDC_Area ); // *** Square Pica Point (1/72 inch)
2182 NEWD( "picapt2", 8.0352160704321409E06, CDC_Area ); // *** Square Pica Point (1/72 inch)
2183 NEWD( "pica2", 5.58001116002232E04, CDC_Area ); // *** Square Pica (1/6 inch)
2184 NEWD( "Morgen", 4.0000000000000000E-04, CDC_Area ); // *** Morgen
2185 NEWDP( "ar", 1.000000E-02, CDC_Area ); // *** Ar
2186 NEWD( "acre", 2.471053815E-04, CDC_Area ); // *** Acre
2187 NEWD( "uk_acre", 2.4710538146716534E-04, CDC_Area ); // *** International acre
2188 NEWD( "us_acre", 2.4710439304662790E-04, CDC_Area ); // *** U.S. survey/statute acre
2189 NEWD( "ly2", 1.1172985860549147E-32, CDC_Area ); // *** Square Light-year
2190 NEWD( "ha", 1.000000E-04, CDC_Area ); // *** Hectare
2192 // SPEED: 1 Meter per Second is...
2193 NEWDP( "m/s", 1.0000000000000000E00, CDC_Speed ); // *** Meters per Second
2194 NEWDP( "m/sec", 1.0000000000000000E00, CDC_Speed ); // *** Meters per Second also
2195 NEWDP( "m/h", 3.6000000000000000E03, CDC_Speed ); // *** Meters per Hour
2196 NEWDP( "m/hr", 3.6000000000000000E03, CDC_Speed ); // *** Meters per Hour also
2197 NEWD( "mph", 2.2369362920544023E00, CDC_Speed ); // *** Britsh Miles per Hour
2198 NEWD( "kn", 1.9438444924406048E00, CDC_Speed ); // *** Knot = Nautical Miles per Hour
2199 NEWD( "admkn", 1.9438446603753486E00, CDC_Speed ); // *** Admiralty Knot
2200 NEWD( "ludicrous speed", 2.0494886343432328E-14, CDC_Speed ); // ***
2201 NEWD( "ridiculous speed", 4.0156958471424288E-06, CDC_Speed); // ***
2203 // INFORMATION: 1 Bit is...
2204 NEWDP( "bit", 1.00E00, CDC_Information); // *** Bit
2205 NEWDP( "byte", 1.25E-01, CDC_Information); // *** Byte
2207 assert(maVector.size() == expected_size);
2211 ConvertDataList::~ConvertDataList() = default;
2214 double ConvertDataList::Convert( double fVal, const OUString& rFrom, const OUString& rTo )
2216 ConvertData* pFrom = nullptr;
2217 ConvertData* pTo = nullptr;
2218 bool bSearchFrom = true;
2219 bool bSearchTo = true;
2220 sal_Int16 nLevelFrom = 0;
2221 sal_Int16 nLevelTo = 0;
2223 for( const auto& rItem : maVector )
2225 if( bSearchFrom )
2227 sal_Int16 n = rItem->GetMatchingLevel(rFrom);
2228 if( n != INV_MATCHLEV )
2230 pFrom = rItem.get();
2231 nLevelFrom = n;
2232 if (!n)
2233 { // only first match for partial equality rulz a little bit more
2234 // ... but exact match rulz most
2235 bSearchFrom = false;
2240 if( bSearchTo )
2242 sal_Int16 n = rItem->GetMatchingLevel(rTo);
2243 if( n != INV_MATCHLEV )
2245 pTo = rItem.get();
2246 nLevelTo = n;
2247 if (!n)
2248 { // only first match for partial equality rulz a little bit more
2249 // ... but exact match rulz most
2250 bSearchTo = false;
2255 if( !bSearchFrom && !bSearchTo )
2256 break;
2259 if( !pFrom || !pTo )
2260 throw lang::IllegalArgumentException();
2262 if (pFrom->Class() != pTo->Class())
2263 throw lang::IllegalArgumentException();
2265 return pFrom->Convert( fVal, *pTo, nLevelFrom, nLevelTo );
2269 ScaDate::ScaDate() :
2270 nOrigDay( 1 ),
2271 nDay( 1 ),
2272 nMonth( 1 ),
2273 nYear( 1900 ),
2274 bLastDayMode( true ),
2275 bLastDay( false ),
2276 b30Days( false ),
2277 bUSMode( false )
2281 ScaDate::ScaDate( sal_Int32 nNullDate, sal_Int32 nDate, sal_Int32 nBase )
2283 DaysToDate( nNullDate + nDate, nOrigDay, nMonth, nYear );
2284 bLastDayMode = (nBase != 5);
2285 bLastDay = (nOrigDay >= ::DaysInMonth( nMonth, nYear ));
2286 b30Days = (nBase == 0) || (nBase == 4);
2287 bUSMode = (nBase == 0);
2288 setDay();
2291 ScaDate::ScaDate( const ScaDate& rCopy ) :
2292 nOrigDay( rCopy.nOrigDay ),
2293 nDay( rCopy.nDay ),
2294 nMonth( rCopy.nMonth ),
2295 nYear( rCopy.nYear ),
2296 bLastDayMode( rCopy.bLastDayMode ),
2297 bLastDay( rCopy.bLastDay ),
2298 b30Days( rCopy.b30Days ),
2299 bUSMode( rCopy.bUSMode )
2303 ScaDate& ScaDate::operator=( const ScaDate& rCopy )
2305 if( this != &rCopy )
2307 nOrigDay = rCopy.nOrigDay;
2308 nDay = rCopy.nDay;
2309 nMonth = rCopy.nMonth;
2310 nYear = rCopy.nYear;
2311 bLastDayMode = rCopy.bLastDayMode;
2312 bLastDay = rCopy.bLastDay;
2313 b30Days = rCopy.b30Days;
2314 bUSMode = rCopy.bUSMode;
2316 return *this;
2319 void ScaDate::setDay()
2321 if( b30Days )
2323 // 30-days-mode: set nDay to 30 if original was last day in month
2324 nDay = std::min( nOrigDay, static_cast< sal_uInt16 >( 30 ) );
2325 if( bLastDay || (nDay >= ::DaysInMonth( nMonth, nYear )) )
2326 nDay = 30;
2328 else
2330 // set nDay to last day in this month if original was last day
2331 sal_uInt16 nLastDay = ::DaysInMonth( nMonth, nYear );
2332 nDay = bLastDay ? nLastDay : std::min( nOrigDay, nLastDay );
2336 sal_Int32 ScaDate::getDaysInMonthRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const
2338 if( nFrom > nTo )
2339 return 0;
2341 sal_Int32 nRet = 0;
2342 if( b30Days )
2343 nRet = (nTo - nFrom + 1) * 30;
2344 else
2346 for( sal_uInt16 nMonthIx = nFrom; nMonthIx <= nTo; ++nMonthIx )
2347 nRet += getDaysInMonth( nMonthIx );
2349 return nRet;
2352 sal_Int32 ScaDate::getDaysInYearRange( sal_uInt16 nFrom, sal_uInt16 nTo ) const
2354 if( nFrom > nTo )
2355 return 0;
2357 return b30Days ? ((nTo - nFrom + 1) * 360) : ::GetDaysInYears( nFrom, nTo );
2360 void ScaDate::doAddYears( sal_Int32 nYearCount )
2362 sal_Int32 nNewYear = nYearCount + nYear;
2363 if( (nNewYear < 0) || (nNewYear > 0x7FFF) )
2364 throw lang::IllegalArgumentException();
2365 nYear = static_cast< sal_uInt16 >( nNewYear );
2368 void ScaDate::addMonths( sal_Int32 nMonthCount )
2370 sal_Int32 nNewMonth = nMonthCount + nMonth;
2371 if( nNewMonth > 12 )
2373 --nNewMonth;
2374 doAddYears( nNewMonth / 12 );
2375 nMonth = static_cast< sal_uInt16 >( nNewMonth % 12 ) + 1;
2377 else if( nNewMonth < 1 )
2379 doAddYears( nNewMonth / 12 - 1 );
2380 nMonth = static_cast< sal_uInt16 >( nNewMonth % 12 + 12 );
2382 else
2383 nMonth = static_cast< sal_uInt16 >( nNewMonth );
2384 setDay();
2387 sal_Int32 ScaDate::getDate( sal_Int32 nNullDate ) const
2389 sal_uInt16 nLastDay = ::DaysInMonth( nMonth, nYear );
2390 sal_uInt16 nRealDay = (bLastDayMode && bLastDay) ? nLastDay : std::min( nLastDay, nOrigDay );
2391 return ::DateToDays( nRealDay, nMonth, nYear ) - nNullDate;
2394 sal_Int32 ScaDate::getDiff( const ScaDate& rFrom, const ScaDate& rTo )
2396 if( rFrom > rTo )
2397 return getDiff( rTo, rFrom );
2399 sal_Int32 nDiff = 0;
2400 ScaDate aFrom( rFrom );
2401 ScaDate aTo( rTo );
2403 if( rTo.b30Days )
2405 // corrections for base 0 (US NASD)
2406 if( rTo.bUSMode )
2408 if( ((rFrom.nMonth == 2) || (rFrom.nDay < 30)) && (aTo.nOrigDay == 31) )
2409 aTo.nDay = 31;
2410 else if( (aTo.nMonth == 2) && aTo.bLastDay )
2411 aTo.nDay = ::DaysInMonth( 2, aTo.nYear );
2413 // corrections for base 4 (Europe)
2414 else
2416 if( (aFrom.nMonth == 2) && (aFrom.nDay == 30) )
2417 aFrom.nDay = ::DaysInMonth( 2, aFrom.nYear );
2418 if( (aTo.nMonth == 2) && (aTo.nDay == 30) )
2419 aTo.nDay = ::DaysInMonth( 2, aTo.nYear );
2423 if( (aFrom.nYear < aTo.nYear) || ((aFrom.nYear == aTo.nYear) && (aFrom.nMonth < aTo.nMonth)) )
2425 // move aFrom to 1st day of next month
2426 nDiff = aFrom.getDaysInMonth() - aFrom.nDay + 1;
2427 aFrom.nOrigDay = aFrom.nDay = 1;
2428 aFrom.bLastDay = false;
2429 aFrom.addMonths( 1 );
2431 if( aFrom.nYear < aTo.nYear )
2433 // move aFrom to 1st day of next year
2434 nDiff += aFrom.getDaysInMonthRange( aFrom.nMonth, 12 );
2435 aFrom.addMonths( 13 - aFrom.nMonth );
2437 // move aFrom to 1st day of this year
2438 nDiff += aFrom.getDaysInYearRange( aFrom.nYear, aTo.nYear - 1 );
2439 aFrom.addYears( aTo.nYear - aFrom.nYear );
2442 // move aFrom to 1st day of this month
2443 nDiff += aFrom.getDaysInMonthRange( aFrom.nMonth, aTo.nMonth - 1 );
2444 aFrom.addMonths( aTo.nMonth - aFrom.nMonth );
2446 // finally add remaining days in this month
2447 nDiff += aTo.nDay - aFrom.nDay;
2448 return std::max<sal_Int32>(nDiff, 0);
2451 bool ScaDate::operator<( const ScaDate& rCmp ) const
2453 if( nYear != rCmp.nYear )
2454 return nYear < rCmp.nYear;
2455 if( nMonth != rCmp.nMonth )
2456 return nMonth < rCmp.nMonth;
2457 if( nDay != rCmp.nDay )
2458 return nDay < rCmp.nDay;
2459 if( bLastDay || rCmp.bLastDay )
2460 return !bLastDay && rCmp.bLastDay;
2461 return nOrigDay < rCmp.nOrigDay;
2465 ScaAnyConverter::ScaAnyConverter( const uno::Reference< uno::XComponentContext >& xContext )
2466 : nDefaultFormat(0)
2467 , bHasValidFormat(false)
2469 xFormatter = util::NumberFormatter::create(xContext);
2472 ScaAnyConverter::~ScaAnyConverter()
2476 void ScaAnyConverter::init( const uno::Reference< beans::XPropertySet >& xPropSet )
2478 // try to get default number format
2479 bHasValidFormat = false;
2480 if( !xFormatter.is() )
2481 return;
2483 // get XFormatsSupplier from outer XPropertySet
2484 uno::Reference< util::XNumberFormatsSupplier > xFormatsSupp( xPropSet, uno::UNO_QUERY );
2485 if( !xFormatsSupp.is() )
2486 return;
2488 // get XNumberFormatTypes from XNumberFormatsSupplier to get standard index
2489 uno::Reference< util::XNumberFormats > xFormats( xFormatsSupp->getNumberFormats() );
2490 uno::Reference< util::XNumberFormatTypes > xFormatTypes( xFormats, uno::UNO_QUERY );
2491 if( xFormatTypes.is() )
2493 lang::Locale eLocale;
2494 nDefaultFormat = xFormatTypes->getStandardIndex( eLocale );
2495 xFormatter->attachNumberFormatsSupplier( xFormatsSupp );
2496 bHasValidFormat = true;
2500 double ScaAnyConverter::convertToDouble( const OUString& rString ) const
2502 double fValue = 0.0;
2503 if( bHasValidFormat )
2507 fValue = xFormatter->convertStringToNumber( nDefaultFormat, rString );
2509 catch( uno::Exception& )
2511 throw lang::IllegalArgumentException();
2514 else
2516 rtl_math_ConversionStatus eStatus;
2517 sal_Int32 nEnd;
2518 fValue = ::rtl::math::stringToDouble( rString, '.', ',', &eStatus, &nEnd );
2519 if( (eStatus != rtl_math_ConversionStatus_Ok) || (nEnd < rString.getLength()) )
2520 throw lang::IllegalArgumentException();
2522 return fValue;
2525 bool ScaAnyConverter::getDouble(
2526 double& rfResult,
2527 const uno::Any& rAny ) const
2529 rfResult = 0.0;
2530 bool bContainsVal = true;
2531 switch( rAny.getValueTypeClass() )
2533 case uno::TypeClass_VOID:
2534 bContainsVal = false;
2535 break;
2536 case uno::TypeClass_STRING:
2538 auto pString = o3tl::forceAccess< OUString >( rAny );
2539 if( !pString->isEmpty() )
2540 rfResult = convertToDouble( *pString );
2541 else
2542 bContainsVal = false;
2544 break;
2545 case uno::TypeClass_HYPER:
2546 rfResult = rAny.get<sal_Int64>();
2547 break;
2548 case uno::TypeClass_UNSIGNED_HYPER:
2549 rfResult = rAny.get<sal_uInt64>();
2550 break;
2551 default:
2552 if( !( rAny >>= rfResult ) )
2553 throw lang::IllegalArgumentException();
2556 return bContainsVal;
2559 bool ScaAnyConverter::getDouble(
2560 double& rfResult,
2561 const uno::Reference< beans::XPropertySet >& xPropSet,
2562 const uno::Any& rAny )
2564 init( xPropSet );
2565 return getDouble( rfResult, rAny );
2568 double ScaAnyConverter::getDouble(
2569 const uno::Reference< beans::XPropertySet >& xPropSet,
2570 const uno::Any& rAny,
2571 double fDefault )
2573 double fResult;
2574 if( !getDouble( fResult, xPropSet, rAny ) )
2575 fResult = fDefault;
2576 return fResult;
2579 bool ScaAnyConverter::getInt32(
2580 sal_Int32& rnResult,
2581 const uno::Reference< beans::XPropertySet >& xPropSet,
2582 const uno::Any& rAny )
2584 double fResult;
2585 bool bContainsVal = getDouble( fResult, xPropSet, rAny );
2586 if( (fResult <= -2147483649.0) || (fResult >= 2147483648.0) )
2587 throw lang::IllegalArgumentException();
2589 rnResult = static_cast< sal_Int32 >( fResult );
2590 return bContainsVal;
2593 sal_Int32 ScaAnyConverter::getInt32(
2594 const uno::Reference< beans::XPropertySet >& xPropSet,
2595 const uno::Any& rAny,
2596 sal_Int32 nDefault )
2598 sal_Int32 nResult;
2599 if( !getInt32( nResult, xPropSet, rAny ) )
2600 nResult = nDefault;
2601 return nResult;
2606 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */