1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 //------------------------------------------------------------------ 25 // 26 // date functions add in 27 // 28 //------------------------------------------------------------------ 29 30 #include "datefunc.hxx" 31 #ifndef _SCA_DATEFUNC_HRC 32 #include "datefunc.hrc" 33 #endif 34 #include <cppuhelper/factory.hxx> 35 #include <osl/diagnose.h> 36 #include <rtl/ustrbuf.hxx> 37 #include <tools/resmgr.hxx> 38 #include <tools/rcid.h> 39 #include <com/sun/star/util/Date.hpp> 40 41 using namespace ::com::sun::star; 42 using namespace ::rtl; 43 44 //------------------------------------------------------------------ 45 46 #define ADDIN_SERVICE "com.sun.star.sheet.AddIn" 47 #define MY_SERVICE "com.sun.star.sheet.addin.DateFunctions" 48 #define MY_IMPLNAME "com.sun.star.sheet.addin.DateFunctionsImpl" 49 50 //------------------------------------------------------------------ 51 52 #define STR_FROM_ANSI( s ) OUString( s, strlen( s ), RTL_TEXTENCODING_MS_1252 ) 53 54 //------------------------------------------------------------------ 55 56 const sal_uInt32 ScaList::nStartSize = 16; 57 const sal_uInt32 ScaList::nIncrSize = 16; 58 59 ScaList::ScaList() : 60 pData( new void*[ nStartSize ] ), 61 nSize( nStartSize ), 62 nCount( 0 ), 63 nCurr( 0 ) 64 { 65 } 66 67 ScaList::~ScaList() 68 { 69 delete[] pData; 70 } 71 72 void ScaList::_Grow() 73 { 74 nSize += nIncrSize; 75 76 void** pNewData = new void*[ nSize ]; 77 memcpy( pNewData, pData, nCount * sizeof( void* ) ); 78 79 delete[] pData; 80 pData = pNewData; 81 } 82 83 void ScaList::Insert( void* pNew, sal_uInt32 nIndex ) 84 { 85 if( nIndex >= nCount ) 86 Append( pNew ); 87 else 88 { 89 Grow(); 90 91 void** pIns = pData + nIndex; 92 memmove( pIns + 1, pIns, (nCount - nIndex) * sizeof( void* ) ); 93 94 *pIns = pNew; 95 nCount++; 96 } 97 } 98 99 100 //------------------------------------------------------------------ 101 102 ScaStringList::~ScaStringList() 103 { 104 for( OUString* pStr = First(); pStr; pStr = Next() ) 105 delete pStr; 106 } 107 108 //------------------------------------------------------------------ 109 110 ScaResId::ScaResId( sal_uInt16 nId, ResMgr& rResMgr ) : 111 ResId( nId, rResMgr ) 112 { 113 } 114 115 116 //------------------------------------------------------------------ 117 118 #define UNIQUE sal_False // function name does not exist in Calc 119 #define DOUBLE sal_True // function name exists in Calc 120 121 #define STDPAR sal_False // all parameters are described 122 #define INTPAR sal_True // first parameter is internal 123 124 #define FUNCDATA( FuncName, ParamCount, Category, Double, IntPar ) \ 125 { "get" #FuncName, DATE_FUNCNAME_##FuncName, DATE_FUNCDESC_##FuncName, DATE_DEFFUNCNAME_##FuncName, ParamCount, Category, Double, IntPar } 126 127 const ScaFuncDataBase pFuncDataArr[] = 128 { 129 FUNCDATA( DiffWeeks, 3, ScaCat_DateTime, UNIQUE, INTPAR ), 130 FUNCDATA( DiffMonths, 3, ScaCat_DateTime, UNIQUE, INTPAR ), 131 FUNCDATA( DiffYears, 3, ScaCat_DateTime, UNIQUE, INTPAR ), 132 FUNCDATA( IsLeapYear, 1, ScaCat_DateTime, UNIQUE, INTPAR ), 133 FUNCDATA( DaysInMonth, 1, ScaCat_DateTime, UNIQUE, INTPAR ), 134 FUNCDATA( DaysInYear, 1, ScaCat_DateTime, UNIQUE, INTPAR ), 135 FUNCDATA( WeeksInYear, 1, ScaCat_DateTime, UNIQUE, INTPAR ), 136 FUNCDATA( Rot13, 1, ScaCat_Text, UNIQUE, STDPAR ) 137 }; 138 139 #undef FUNCDATA 140 141 142 //------------------------------------------------------------------ 143 144 ScaFuncData::ScaFuncData( const ScaFuncDataBase& rBaseData, ResMgr& rResMgr ) : 145 aIntName( OUString::createFromAscii( rBaseData.pIntName ) ), 146 nUINameID( rBaseData.nUINameID ), 147 nDescrID( rBaseData.nDescrID ), 148 nCompListID( rBaseData.nCompListID ), 149 nParamCount( rBaseData.nParamCount ), 150 eCat( rBaseData.eCat ), 151 bDouble( rBaseData.bDouble ), 152 bWithOpt( rBaseData.bWithOpt ) 153 { 154 ScaResStringArrLoader aArrLoader( RID_DATE_DEFFUNCTION_NAMES, nCompListID, rResMgr ); 155 const ResStringArray& rArr = aArrLoader.GetStringArray(); 156 157 for( sal_uInt16 nIndex = 0; nIndex < rArr.Count(); nIndex++ ) 158 aCompList.Append( rArr.GetString( nIndex ) ); 159 } 160 161 ScaFuncData::~ScaFuncData() 162 { 163 } 164 165 sal_uInt16 ScaFuncData::GetStrIndex( sal_uInt16 nParam ) const 166 { 167 if( !bWithOpt ) 168 nParam++; 169 return (nParam > nParamCount) ? (nParamCount * 2) : (nParam * 2); 170 } 171 172 173 //------------------------------------------------------------------ 174 175 ScaFuncDataList::ScaFuncDataList( ResMgr& rResMgr ) : 176 nLast( 0xFFFFFFFF ) 177 { 178 const sal_uInt32 nCnt = sizeof( pFuncDataArr ) / sizeof( ScaFuncDataBase ); 179 180 for( sal_uInt16 nIndex = 0; nIndex < nCnt; nIndex++ ) 181 Append( new ScaFuncData( pFuncDataArr[ nIndex ], rResMgr ) ); 182 } 183 184 ScaFuncDataList::~ScaFuncDataList() 185 { 186 for( ScaFuncData* pFData = First(); pFData; pFData = Next() ) 187 delete pFData; 188 } 189 190 const ScaFuncData* ScaFuncDataList::Get( const OUString& rProgrammaticName ) const 191 { 192 if( aLastName == rProgrammaticName ) 193 return Get( nLast ); 194 195 for( sal_uInt32 nIndex = 0; nIndex < Count(); nIndex++ ) 196 { 197 const ScaFuncData* pCurr = Get( nIndex ); 198 if( pCurr->Is( rProgrammaticName ) ) 199 { 200 const_cast< ScaFuncDataList* >( this )->aLastName = rProgrammaticName; 201 const_cast< ScaFuncDataList* >( this )->nLast = nIndex; 202 return pCurr; 203 } 204 } 205 return NULL; 206 } 207 208 209 //------------------------------------------------------------------ 210 211 ScaFuncRes::ScaFuncRes( ResId& rResId, ResMgr& rResMgr, sal_uInt16 nIndex, OUString& rRet ) : 212 Resource( rResId ) 213 { 214 rRet = String( ScaResId( nIndex, rResMgr ) ); 215 FreeResource(); 216 } 217 218 219 //------------------------------------------------------------------ 220 // 221 // entry points for service registration / instantiation 222 // 223 //------------------------------------------------------------------ 224 225 uno::Reference< uno::XInterface > SAL_CALL ScaDateAddIn_CreateInstance( 226 const uno::Reference< lang::XMultiServiceFactory >& ) 227 { 228 static uno::Reference< uno::XInterface > xInst = (cppu::OWeakObject*) new ScaDateAddIn(); 229 return xInst; 230 } 231 232 233 //------------------------------------------------------------------------ 234 235 extern "C" { 236 237 void SAL_CALL component_getImplementationEnvironment( 238 const sal_Char ** ppEnvTypeName, uno_Environment ** /*ppEnv*/ ) 239 { 240 *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; 241 } 242 243 void * SAL_CALL component_getFactory( 244 const sal_Char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ ) 245 { 246 void* pRet = 0; 247 248 if ( pServiceManager && 249 OUString::createFromAscii( pImplName ) == ScaDateAddIn::getImplementationName_Static() ) 250 { 251 uno::Reference< lang::XSingleServiceFactory > xFactory( cppu::createOneInstanceFactory( 252 reinterpret_cast< lang::XMultiServiceFactory* >( pServiceManager ), 253 ScaDateAddIn::getImplementationName_Static(), 254 ScaDateAddIn_CreateInstance, 255 ScaDateAddIn::getSupportedServiceNames_Static() ) ); 256 257 if (xFactory.is()) 258 { 259 xFactory->acquire(); 260 pRet = xFactory.get(); 261 } 262 } 263 264 return pRet; 265 } 266 267 } // extern C 268 269 //------------------------------------------------------------------------ 270 // 271 // "normal" service implementation 272 // 273 //------------------------------------------------------------------------ 274 275 ScaDateAddIn::ScaDateAddIn() : 276 pDefLocales( NULL ), 277 pResMgr( NULL ), 278 pFuncDataList( NULL ) 279 { 280 } 281 282 ScaDateAddIn::~ScaDateAddIn() 283 { 284 if( pFuncDataList ) 285 delete pFuncDataList; 286 if( pDefLocales ) 287 delete[] pDefLocales; 288 289 // pResMgr already deleted (_all_ resource managers are deleted _before_ this dtor is called) 290 } 291 292 static const sal_Char* pLang[] = { "de", "en" }; 293 static const sal_Char* pCoun[] = { "DE", "US" }; 294 static const sal_uInt32 nNumOfLoc = sizeof( pLang ) / sizeof( sal_Char* ); 295 296 void ScaDateAddIn::InitDefLocales() 297 { 298 pDefLocales = new lang::Locale[ nNumOfLoc ]; 299 300 for( sal_uInt32 nIndex = 0; nIndex < nNumOfLoc; nIndex++ ) 301 { 302 pDefLocales[ nIndex ].Language = OUString::createFromAscii( pLang[ nIndex ] ); 303 pDefLocales[ nIndex ].Country = OUString::createFromAscii( pCoun[ nIndex ] ); 304 } 305 } 306 307 const lang::Locale& ScaDateAddIn::GetLocale( sal_uInt32 nIndex ) 308 { 309 if( !pDefLocales ) 310 InitDefLocales(); 311 312 return (nIndex < sizeof( pLang )) ? pDefLocales[ nIndex ] : aFuncLoc; 313 } 314 315 ResMgr& ScaDateAddIn::GetResMgr() throw( uno::RuntimeException ) 316 { 317 if( !pResMgr ) 318 { 319 InitData(); // try to get resource manager 320 if( !pResMgr ) 321 throw uno::RuntimeException(); 322 } 323 return *pResMgr; 324 } 325 326 void ScaDateAddIn::InitData() 327 { 328 if( pResMgr ) 329 delete pResMgr; 330 331 OString aModName( "date" ); 332 pResMgr = ResMgr::CreateResMgr( (const sal_Char*) aModName, 333 aFuncLoc ); 334 335 if( pFuncDataList ) 336 delete pFuncDataList; 337 338 pFuncDataList = pResMgr ? new ScaFuncDataList( *pResMgr ) : NULL; 339 340 if( pDefLocales ) 341 { 342 delete pDefLocales; 343 pDefLocales = NULL; 344 } 345 } 346 347 OUString ScaDateAddIn::GetDisplFuncStr( sal_uInt16 nResId ) throw( uno::RuntimeException ) 348 { 349 return ScaResStringLoader( RID_DATE_FUNCTION_NAMES, nResId, GetResMgr() ).GetString(); 350 } 351 352 OUString ScaDateAddIn::GetFuncDescrStr( sal_uInt16 nResId, sal_uInt16 nStrIndex ) throw( uno::RuntimeException ) 353 { 354 OUString aRet; 355 356 ScaResPublisher aResPubl( ScaResId( RID_DATE_FUNCTION_DESCRIPTIONS, GetResMgr() ) ); 357 ScaResId aResId( nResId, GetResMgr() ); 358 aResId.SetRT( RSC_RESOURCE ); 359 360 if( aResPubl.IsAvailableRes( aResId ) ) 361 ScaFuncRes aSubRes( aResId, GetResMgr(), nStrIndex, aRet ); 362 363 aResPubl.FreeResource(); 364 return aRet; 365 } 366 367 368 //------------------------------------------------------------------------ 369 370 OUString ScaDateAddIn::getImplementationName_Static() 371 { 372 return OUString::createFromAscii( MY_IMPLNAME ); 373 } 374 375 uno::Sequence< OUString > ScaDateAddIn::getSupportedServiceNames_Static() 376 { 377 uno::Sequence< OUString > aRet( 2 ); 378 OUString* pArray = aRet.getArray(); 379 pArray[0] = OUString::createFromAscii( ADDIN_SERVICE ); 380 pArray[1] = OUString::createFromAscii( MY_SERVICE ); 381 return aRet; 382 } 383 384 // XServiceName 385 386 OUString SAL_CALL ScaDateAddIn::getServiceName() throw( uno::RuntimeException ) 387 { 388 // name of specific AddIn service 389 return OUString::createFromAscii( MY_SERVICE ); 390 } 391 392 // XServiceInfo 393 394 OUString SAL_CALL ScaDateAddIn::getImplementationName() throw( uno::RuntimeException ) 395 { 396 return getImplementationName_Static(); 397 } 398 399 sal_Bool SAL_CALL ScaDateAddIn::supportsService( const OUString& aServiceName ) throw( uno::RuntimeException ) 400 { 401 return aServiceName.equalsAscii( ADDIN_SERVICE ) || 402 aServiceName.equalsAscii( MY_SERVICE ); 403 } 404 405 uno::Sequence< OUString > SAL_CALL ScaDateAddIn::getSupportedServiceNames() throw( uno::RuntimeException ) 406 { 407 return getSupportedServiceNames_Static(); 408 } 409 410 // XLocalizable 411 412 void SAL_CALL ScaDateAddIn::setLocale( const lang::Locale& eLocale ) throw( uno::RuntimeException ) 413 { 414 aFuncLoc = eLocale; 415 InitData(); // change of locale invalidates resources! 416 } 417 418 lang::Locale SAL_CALL ScaDateAddIn::getLocale() throw( uno::RuntimeException ) 419 { 420 return aFuncLoc; 421 } 422 423 //------------------------------------------------------------------ 424 // 425 // function descriptions start here 426 // 427 //------------------------------------------------------------------ 428 429 // XAddIn 430 431 OUString SAL_CALL ScaDateAddIn::getProgrammaticFuntionName( const OUString& ) throw( uno::RuntimeException ) 432 { 433 // not used by calc 434 // (but should be implemented for other uses of the AddIn service) 435 return OUString(); 436 } 437 438 OUString SAL_CALL ScaDateAddIn::getDisplayFunctionName( const OUString& aProgrammaticName ) throw( uno::RuntimeException ) 439 { 440 OUString aRet; 441 442 const ScaFuncData* pFData = pFuncDataList->Get( aProgrammaticName ); 443 if( pFData ) 444 { 445 aRet = GetDisplFuncStr( pFData->GetUINameID() ); 446 if( pFData->IsDouble() ) 447 aRet += STR_FROM_ANSI( "_ADD" ); 448 } 449 else 450 { 451 aRet = STR_FROM_ANSI( "UNKNOWNFUNC_" ); 452 aRet += aProgrammaticName; 453 } 454 455 return aRet; 456 } 457 458 OUString SAL_CALL ScaDateAddIn::getFunctionDescription( const OUString& aProgrammaticName ) throw( uno::RuntimeException ) 459 { 460 OUString aRet; 461 462 const ScaFuncData* pFData = pFuncDataList->Get( aProgrammaticName ); 463 if( pFData ) 464 aRet = GetFuncDescrStr( pFData->GetDescrID(), 1 ); 465 466 return aRet; 467 } 468 469 OUString SAL_CALL ScaDateAddIn::getDisplayArgumentName( 470 const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException ) 471 { 472 OUString aRet; 473 474 const ScaFuncData* pFData = pFuncDataList->Get( aProgrammaticName ); 475 if( pFData && (nArgument <= 0xFFFF) ) 476 { 477 sal_uInt16 nStr = pFData->GetStrIndex( static_cast< sal_uInt16 >( nArgument ) ); 478 if( nStr ) 479 aRet = GetFuncDescrStr( pFData->GetDescrID(), nStr ); 480 else 481 aRet = STR_FROM_ANSI( "internal" ); 482 } 483 484 return aRet; 485 } 486 487 OUString SAL_CALL ScaDateAddIn::getArgumentDescription( 488 const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException ) 489 { 490 OUString aRet; 491 492 const ScaFuncData* pFData = pFuncDataList->Get( aProgrammaticName ); 493 if( pFData && (nArgument <= 0xFFFF) ) 494 { 495 sal_uInt16 nStr = pFData->GetStrIndex( static_cast< sal_uInt16 >( nArgument ) ); 496 if( nStr ) 497 aRet = GetFuncDescrStr( pFData->GetDescrID(), nStr + 1 ); 498 else 499 aRet = STR_FROM_ANSI( "for internal use only" ); 500 } 501 502 return aRet; 503 } 504 505 OUString SAL_CALL ScaDateAddIn::getProgrammaticCategoryName( 506 const OUString& aProgrammaticName ) throw( uno::RuntimeException ) 507 { 508 OUString aRet; 509 510 const ScaFuncData* pFData = pFuncDataList->Get( aProgrammaticName ); 511 if( pFData ) 512 { 513 switch( pFData->GetCategory() ) 514 { 515 case ScaCat_DateTime: aRet = STR_FROM_ANSI( "Date&Time" ); break; 516 case ScaCat_Text: aRet = STR_FROM_ANSI( "Text" ); break; 517 case ScaCat_Finance: aRet = STR_FROM_ANSI( "Financial" ); break; 518 case ScaCat_Inf: aRet = STR_FROM_ANSI( "Information" ); break; 519 case ScaCat_Math: aRet = STR_FROM_ANSI( "Mathematical" ); break; 520 case ScaCat_Tech: aRet = STR_FROM_ANSI( "Technical" ); break; 521 default: // to prevent compiler warnings 522 break; 523 } 524 } 525 526 if( !aRet.getLength() ) 527 aRet = STR_FROM_ANSI( "Add-In" ); 528 return aRet; 529 } 530 531 OUString SAL_CALL ScaDateAddIn::getDisplayCategoryName( 532 const OUString& aProgrammaticName ) throw( uno::RuntimeException ) 533 { 534 return getProgrammaticCategoryName( aProgrammaticName ); 535 } 536 537 538 // XCompatibilityNames 539 540 uno::Sequence< sheet::LocalizedName > SAL_CALL ScaDateAddIn::getCompatibilityNames( 541 const OUString& aProgrammaticName ) throw( uno::RuntimeException ) 542 { 543 const ScaFuncData* pFData = pFuncDataList->Get( aProgrammaticName ); 544 if( !pFData ) 545 return uno::Sequence< sheet::LocalizedName >( 0 ); 546 547 const ScaStringList& rStrList = pFData->GetCompNameList(); 548 sal_uInt32 nCount = rStrList.Count(); 549 550 uno::Sequence< sheet::LocalizedName > aRet( nCount ); 551 sheet::LocalizedName* pArray = aRet.getArray(); 552 553 for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ ) 554 pArray[ nIndex ] = sheet::LocalizedName( GetLocale( nIndex ), *rStrList.Get( nIndex ) ); 555 556 return aRet; 557 } 558 559 560 //------------------------------------------------------------------ 561 // 562 // function implementation starts here 563 // 564 //------------------------------------------------------------------ 565 566 // auxiliary functions 567 568 sal_Bool IsLeapYear( sal_uInt16 nYear ) 569 { 570 return ((((nYear % 4) == 0) && ((nYear % 100) != 0)) || ((nYear % 400) == 0)); 571 } 572 573 sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear ) 574 { 575 static sal_uInt16 aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30, 576 31, 31, 30, 31, 30, 31 }; 577 578 if ( nMonth != 2 ) 579 return aDaysInMonth[nMonth-1]; 580 else 581 { 582 if ( IsLeapYear(nYear) ) 583 return aDaysInMonth[nMonth-1] + 1; 584 else 585 return aDaysInMonth[nMonth-1]; 586 } 587 } 588 589 /** 590 * Convert a date to a count of days starting from 01/01/0001 591 * 592 * The internal representation of a Date used in this Addin 593 * is the number of days between 01/01/0001 and the date 594 * this function converts a Day , Month, Year representation 595 * to this internal Date value. 596 */ 597 598 sal_Int32 DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear ) 599 { 600 sal_Int32 nDays = ((sal_Int32)nYear-1) * 365; 601 nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400); 602 603 for( sal_uInt16 i = 1; i < nMonth; i++ ) 604 nDays += DaysInMonth(i,nYear); 605 nDays += nDay; 606 607 return nDays; 608 } 609 610 /** 611 * Convert a count of days starting from 01/01/0001 to a date 612 * 613 * The internal representation of a Date used in this Addin 614 * is the number of days between 01/01/0001 and the date 615 * this function converts this internal Date value 616 * to a Day , Month, Year representation of a Date. 617 */ 618 619 void DaysToDate( sal_Int32 nDays, 620 sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear ) 621 throw( lang::IllegalArgumentException ) 622 { 623 if( nDays < 0 ) 624 throw lang::IllegalArgumentException(); 625 626 sal_Int32 nTempDays; 627 sal_Int32 i = 0; 628 sal_Bool bCalc; 629 630 do 631 { 632 nTempDays = nDays; 633 rYear = (sal_uInt16)((nTempDays / 365) - i); 634 nTempDays -= ((sal_Int32) rYear -1) * 365; 635 nTempDays -= (( rYear -1) / 4) - (( rYear -1) / 100) + ((rYear -1) / 400); 636 bCalc = sal_False; 637 if ( nTempDays < 1 ) 638 { 639 i++; 640 bCalc = sal_True; 641 } 642 else 643 { 644 if ( nTempDays > 365 ) 645 { 646 if ( (nTempDays != 366) || !IsLeapYear( rYear ) ) 647 { 648 i--; 649 bCalc = sal_True; 650 } 651 } 652 } 653 } 654 while ( bCalc ); 655 656 rMonth = 1; 657 while ( (sal_Int32)nTempDays > DaysInMonth( rMonth, rYear ) ) 658 { 659 nTempDays -= DaysInMonth( rMonth, rYear ); 660 rMonth++; 661 } 662 rDay = (sal_uInt16)nTempDays; 663 } 664 665 /** 666 * Get the null date used by the spreadsheet document 667 * 668 * The internal representation of a Date used in this Addin 669 * is the number of days between 01/01/0001 and the date 670 * this function returns this internal Date value for the document null date 671 * 672 */ 673 674 sal_Int32 GetNullDate( const uno::Reference< beans::XPropertySet >& xOptions ) 675 throw( uno::RuntimeException ) 676 { 677 if (xOptions.is()) 678 { 679 try 680 { 681 uno::Any aAny = xOptions->getPropertyValue( 682 OUString::createFromAscii( "NullDate" ) ); 683 util::Date aDate; 684 if ( aAny >>= aDate ) 685 return DateToDays( aDate.Day, aDate.Month, aDate.Year ); 686 } 687 catch (uno::Exception&) 688 { 689 } 690 } 691 692 // no null date available -> no calculations possible 693 throw uno::RuntimeException(); 694 } 695 696 // XDateFunctions 697 698 /** 699 * Get week difference between 2 dates 700 * 701 * new Weeks(date1,date2,mode) function for StarCalc 702 * 703 * Two modes of operation are provided. 704 * The first is just a simple division by 7 calculation. 705 * 706 * The second calculates the diffence by week of year. 707 * 708 * The International Standard IS-8601 has decreed that Monday 709 * shall be the first day of the week. 710 * 711 * A week that lies partly in one year and partly in annother 712 * is assigned a number in the the year in which most of its days lie. 713 * 714 * That means that week 1 of any year is the week that contains the 4. January 715 * 716 * The internal representation of a Date used in the Addin is the number of days based on 01/01/0001 717 * 718 * A WeekDay can be then calculated by substracting 1 and calculating the rest of 719 * a division by 7, which gives a 0 - 6 value for Monday - Sunday 720 * 721 * Using the 4. January rule explained above the formula 722 * 723 * nWeek1= ( nDays1 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1; 724 * 725 * calculates a number between 0-53 for each day which is in the same year as nJan4 726 * where 0 means that this week belonged to the year before. 727 * 728 * If a day in the same or annother year is used in this formula this calculates 729 * an calendar week offset from a given 4. January 730 * 731 * nWeek2 = ( nDays2 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1; 732 * 733 * The 4.January of first Date Argument can thus be used to calculate 734 * the week difference by calendar weeks which is then nWeek = nWeek2 - nWeek1 735 * 736 * which can be optimized to 737 * 738 * nWeek = ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 ) 739 * 740 * Note: All calculations are operating on the long integer data type 741 * % is the modulo operator in C which calculates the rest of an Integer division 742 * 743 * 744 * mode 0 is the interval between the dates in month, that is days / 7 745 * 746 * mode 1 is the difference by week of year 747 * 748 */ 749 750 sal_Int32 SAL_CALL ScaDateAddIn::getDiffWeeks( 751 const uno::Reference< beans::XPropertySet >& xOptions, 752 sal_Int32 nStartDate, sal_Int32 nEndDate, 753 sal_Int32 nMode ) throw( uno::RuntimeException, lang::IllegalArgumentException ) 754 { 755 sal_Int32 nNullDate = GetNullDate( xOptions ); 756 757 sal_Int32 nDays1 = nStartDate + nNullDate; 758 sal_Int32 nDays2 = nEndDate + nNullDate; 759 760 sal_Int32 nRet; 761 762 if ( nMode == 1 ) 763 { 764 sal_uInt16 nDay,nMonth,nYear; 765 DaysToDate( nDays1, nDay, nMonth, nYear ); 766 sal_Int32 nJan4 = DateToDays( 4, 1, nYear ); 767 768 nRet = ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 ); 769 } 770 else 771 { 772 nRet = (nDays2 - nDays1) / 7; 773 } 774 return nRet; 775 } 776 777 /** 778 * Get month difference between 2 dates 779 * =Month(start, end, mode) Function for StarCalc 780 * 781 * two modes are provided 782 * 783 * mode 0 is the interval between the dates in month 784 * 785 * mode 1 is the difference in calendar month 786 */ 787 788 sal_Int32 SAL_CALL ScaDateAddIn::getDiffMonths( 789 const uno::Reference< beans::XPropertySet >& xOptions, 790 sal_Int32 nStartDate, sal_Int32 nEndDate, 791 sal_Int32 nMode ) throw( uno::RuntimeException, lang::IllegalArgumentException ) 792 { 793 sal_Int32 nNullDate = GetNullDate( xOptions ); 794 795 sal_Int32 nDays1 = nStartDate + nNullDate; 796 sal_Int32 nDays2 = nEndDate + nNullDate; 797 798 sal_uInt16 nDay1,nMonth1,nYear1; 799 sal_uInt16 nDay2,nMonth2,nYear2; 800 DaysToDate(nDays1,nDay1,nMonth1,nYear1); 801 DaysToDate(nDays2,nDay2,nMonth2,nYear2); 802 803 sal_Int32 nRet = nMonth2 - nMonth1 + (nYear2 - nYear1) * 12; 804 if ( nMode == 1 || nDays1 == nDays2 ) return nRet; 805 806 if ( nDays1 < nDays2 ) 807 { 808 if ( nDay1 > nDay2 ) 809 { 810 nRet -= 1; 811 } 812 } 813 else 814 { 815 if ( nDay1 < nDay2 ) 816 { 817 nRet += 1; 818 } 819 } 820 821 return nRet; 822 } 823 824 /** 825 * Get Year difference between 2 dates 826 * 827 * two modes are provided 828 * 829 * mode 0 is the interval between the dates in years 830 * 831 * mode 1 is the difference in calendar years 832 */ 833 834 sal_Int32 SAL_CALL ScaDateAddIn::getDiffYears( 835 const uno::Reference< beans::XPropertySet >& xOptions, 836 sal_Int32 nStartDate, sal_Int32 nEndDate, 837 sal_Int32 nMode ) throw( uno::RuntimeException, lang::IllegalArgumentException ) 838 { 839 if ( nMode != 1 ) 840 return getDiffMonths( xOptions, nStartDate, nEndDate, nMode ) / 12; 841 842 sal_Int32 nNullDate = GetNullDate( xOptions ); 843 844 sal_Int32 nDays1 = nStartDate + nNullDate; 845 sal_Int32 nDays2 = nEndDate + nNullDate; 846 847 sal_uInt16 nDay1,nMonth1,nYear1; 848 sal_uInt16 nDay2,nMonth2,nYear2; 849 DaysToDate(nDays1,nDay1,nMonth1,nYear1); 850 DaysToDate(nDays2,nDay2,nMonth2,nYear2); 851 852 return nYear2 - nYear1; 853 } 854 855 /** 856 * Check if a Date is in a leap year in the Gregorian calendar 857 */ 858 859 sal_Int32 SAL_CALL ScaDateAddIn::getIsLeapYear( 860 const uno::Reference< beans::XPropertySet >& xOptions, 861 sal_Int32 nDate ) throw( uno::RuntimeException, lang::IllegalArgumentException ) 862 { 863 sal_Int32 nNullDate = GetNullDate( xOptions ); 864 sal_Int32 nDays = nDate + nNullDate; 865 866 sal_uInt16 nDay, nMonth, nYear; 867 DaysToDate(nDays,nDay,nMonth,nYear); 868 869 return (sal_Int32)IsLeapYear(nYear); 870 } 871 872 /** 873 * Get the Number of Days in the month for a date 874 */ 875 876 sal_Int32 SAL_CALL ScaDateAddIn::getDaysInMonth( 877 const uno::Reference<beans::XPropertySet>& xOptions, 878 sal_Int32 nDate ) throw( uno::RuntimeException, lang::IllegalArgumentException ) 879 { 880 sal_Int32 nNullDate = GetNullDate( xOptions ); 881 sal_Int32 nDays = nDate + nNullDate; 882 883 sal_uInt16 nDay, nMonth, nYear; 884 DaysToDate(nDays,nDay,nMonth,nYear); 885 886 return DaysInMonth( nMonth, nYear ); 887 } 888 889 /** 890 * Get number of days in the year of a date specified 891 */ 892 893 sal_Int32 SAL_CALL ScaDateAddIn::getDaysInYear( 894 const uno::Reference< beans::XPropertySet >& xOptions, 895 sal_Int32 nDate ) throw( uno::RuntimeException, lang::IllegalArgumentException ) 896 { 897 sal_Int32 nNullDate = GetNullDate( xOptions ); 898 sal_Int32 nDays = nDate + nNullDate; 899 900 sal_uInt16 nDay, nMonth, nYear; 901 DaysToDate(nDays,nDay,nMonth,nYear); 902 903 return ( IsLeapYear(nYear) ? 366 : 365 ); 904 } 905 906 /** 907 * Get number of weeks in the year for a date 908 * 909 * Most years have 52 weeks, but years that start on a Thursday 910 * and leep years that start on a Wednesday have 53 weeks 911 * 912 * The International Standard IS-8601 has decreed that Monday 913 * shall be the first day of the week. 914 * 915 * A WeekDay can be calculated by substracting 1 and calculating the rest of 916 * a division by 7 from the internal date represention 917 * which gives a 0 - 6 value for Monday - Sunday 918 * 919 * @see #IsLeapYear #WeekNumber 920 */ 921 922 sal_Int32 SAL_CALL ScaDateAddIn::getWeeksInYear( 923 const uno::Reference< beans::XPropertySet >& xOptions, 924 sal_Int32 nDate ) throw( uno::RuntimeException, lang::IllegalArgumentException ) 925 { 926 sal_Int32 nNullDate = GetNullDate( xOptions ); 927 sal_Int32 nDays = nDate + nNullDate; 928 929 sal_uInt16 nDay, nMonth, nYear; 930 DaysToDate(nDays,nDay,nMonth,nYear); 931 932 sal_Int32 nJan1WeekDay = ( DateToDays(1,1,nYear) - 1) % 7; 933 934 sal_Int32 nRet; 935 if ( nJan1WeekDay == 3 ) /* Thursday */ 936 nRet = 53; 937 else if ( nJan1WeekDay == 2 ) /* Wednesday */ 938 nRet = ( IsLeapYear(nYear) ? 53 : 52 ); 939 else 940 nRet = 52; 941 942 return nRet; 943 } 944 945 /** 946 * Encrypt or decrypt a string using ROT13 algorithm 947 * 948 * This function rotates each character by 13 in the alphabet. 949 * Only the characters 'a' ... 'z' and 'A' ... 'Z' are modified. 950 */ 951 952 OUString SAL_CALL ScaDateAddIn::getRot13( const OUString& aSrcString ) throw( uno::RuntimeException, lang::IllegalArgumentException ) 953 { 954 OUStringBuffer aBuffer( aSrcString ); 955 for( sal_Int32 nIndex = 0; nIndex < aBuffer.getLength(); nIndex++ ) 956 { 957 sal_Unicode cChar = aBuffer.charAt( nIndex ); 958 if( ((cChar >= 'a') && (cChar <= 'z') && ((cChar += 13) > 'z')) || 959 ((cChar >= 'A') && (cChar <= 'Z') && ((cChar += 13) > 'Z')) ) 960 cChar -= 26; 961 aBuffer.setCharAt( nIndex, cChar ); 962 } 963 return aBuffer.makeStringAndClear(); 964 } 965 966 //------------------------------------------------------------------ 967 968