1 /* 2 ******************************************************************************* 3 * Copyright (C) 1997-2015, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 * 7 * File CALENDAR.CPP 8 * 9 * Modification History: 10 * 11 * Date Name Description 12 * 02/03/97 clhuang Creation. 13 * 04/22/97 aliu Cleaned up, fixed memory leak, made 14 * setWeekCountData() more robust. 15 * Moved platform code to TPlatformUtilities. 16 * 05/01/97 aliu Made equals(), before(), after() arguments const. 17 * 05/20/97 aliu Changed logic of when to compute fields and time 18 * to fix bugs. 19 * 08/12/97 aliu Added equivalentTo. Misc other fixes. 20 * 07/28/98 stephen Sync up with JDK 1.2 21 * 09/02/98 stephen Sync with JDK 1.2 8/31 build (getActualMin/Max) 22 * 03/17/99 stephen Changed adoptTimeZone() - now fAreFieldsSet is 23 * set to FALSE to force update of time. 24 ******************************************************************************* 25 */ 26 27 #include "utypeinfo.h" // for 'typeid' to work 28 29 #include "unicode/utypes.h" 30 31 #if !UCONFIG_NO_FORMATTING 32 33 #include "unicode/gregocal.h" 34 #include "unicode/basictz.h" 35 #include "unicode/simpletz.h" 36 #include "unicode/rbtz.h" 37 #include "unicode/vtzone.h" 38 #include "gregoimp.h" 39 #include "buddhcal.h" 40 #include "taiwncal.h" 41 #include "japancal.h" 42 #include "islamcal.h" 43 #include "hebrwcal.h" 44 #include "persncal.h" 45 #include "indiancal.h" 46 #include "chnsecal.h" 47 #include "coptccal.h" 48 #include "dangical.h" 49 #include "ethpccal.h" 50 #include "unicode/calendar.h" 51 #include "cpputils.h" 52 #include "servloc.h" 53 #include "ucln_in.h" 54 #include "cstring.h" 55 #include "locbased.h" 56 #include "uresimp.h" 57 #include "ustrenum.h" 58 #include "uassert.h" 59 #include "olsontz.h" 60 #include "sharedcalendar.h" 61 #include "unifiedcache.h" 62 63 #if !UCONFIG_NO_SERVICE 64 static icu::ICULocaleService* gService = NULL; 65 static icu::UInitOnce gServiceInitOnce = U_INITONCE_INITIALIZER; 66 #endif 67 68 // INTERNAL - for cleanup 69 70 U_CDECL_BEGIN 71 static UBool calendar_cleanup(void) { 72 #if !UCONFIG_NO_SERVICE 73 if (gService) { 74 delete gService; 75 gService = NULL; 76 } 77 gServiceInitOnce.reset(); 78 #endif 79 return TRUE; 80 } 81 U_CDECL_END 82 83 // ------------------------------------------ 84 // 85 // Registration 86 // 87 //------------------------------------------- 88 //#define U_DEBUG_CALSVC 1 89 // 90 91 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) 92 93 /** 94 * fldName was removed as a duplicate implementation. 95 * use udbg_ services instead, 96 * which depend on include files and library from ../tools/toolutil, the following circular link: 97 * CPPFLAGS+=-I$(top_srcdir)/tools/toolutil 98 * LIBS+=$(LIBICUTOOLUTIL) 99 */ 100 #include "udbgutil.h" 101 #include <stdio.h> 102 103 /** 104 * convert a UCalendarDateFields into a string - for debugging 105 * @param f field enum 106 * @return static string to the field name 107 * @internal 108 */ 109 110 const char* fldName(UCalendarDateFields f) { 111 return udbg_enumName(UDBG_UCalendarDateFields, (int32_t)f); 112 } 113 114 #if UCAL_DEBUG_DUMP 115 // from CalendarTest::calToStr - but doesn't modify contents. 116 void ucal_dump(const Calendar &cal) { 117 cal.dump(); 118 } 119 120 void Calendar::dump() const { 121 int i; 122 fprintf(stderr, "@calendar=%s, timeset=%c, fieldset=%c, allfields=%c, virtualset=%c, t=%.2f", 123 getType(), fIsTimeSet?'y':'n', fAreFieldsSet?'y':'n', fAreAllFieldsSet?'y':'n', 124 fAreFieldsVirtuallySet?'y':'n', 125 fTime); 126 127 // can add more things here: DST, zone, etc. 128 fprintf(stderr, "\n"); 129 for(i = 0;i<UCAL_FIELD_COUNT;i++) { 130 int n; 131 const char *f = fldName((UCalendarDateFields)i); 132 fprintf(stderr, " %25s: %-11ld", f, fFields[i]); 133 if(fStamp[i] == kUnset) { 134 fprintf(stderr, " (unset) "); 135 } else if(fStamp[i] == kInternallySet) { 136 fprintf(stderr, " (internally set) "); 137 //} else if(fStamp[i] == kInternalDefault) { 138 // fprintf(stderr, " (internal default) "); 139 } else { 140 fprintf(stderr, " %%%d ", fStamp[i]); 141 } 142 fprintf(stderr, "\n"); 143 144 } 145 } 146 147 U_CFUNC void ucal_dump(UCalendar* cal) { 148 ucal_dump( *((Calendar*)cal) ); 149 } 150 #endif 151 152 #endif 153 154 /* Max value for stamp allowable before recalculation */ 155 #define STAMP_MAX 10000 156 157 static const char * const gCalTypes[] = { 158 "gregorian", 159 "japanese", 160 "buddhist", 161 "roc", 162 "persian", 163 "islamic-civil", 164 "islamic", 165 "hebrew", 166 "chinese", 167 "indian", 168 "coptic", 169 "ethiopic", 170 "ethiopic-amete-alem", 171 "iso8601", 172 "dangi", 173 "islamic-umalqura", 174 "islamic-tbla", 175 "islamic-rgsa", 176 NULL 177 }; 178 179 // Must be in the order of gCalTypes above 180 typedef enum ECalType { 181 CALTYPE_UNKNOWN = -1, 182 CALTYPE_GREGORIAN = 0, 183 CALTYPE_JAPANESE, 184 CALTYPE_BUDDHIST, 185 CALTYPE_ROC, 186 CALTYPE_PERSIAN, 187 CALTYPE_ISLAMIC_CIVIL, 188 CALTYPE_ISLAMIC, 189 CALTYPE_HEBREW, 190 CALTYPE_CHINESE, 191 CALTYPE_INDIAN, 192 CALTYPE_COPTIC, 193 CALTYPE_ETHIOPIC, 194 CALTYPE_ETHIOPIC_AMETE_ALEM, 195 CALTYPE_ISO8601, 196 CALTYPE_DANGI, 197 CALTYPE_ISLAMIC_UMALQURA, 198 CALTYPE_ISLAMIC_TBLA, 199 CALTYPE_ISLAMIC_RGSA 200 } ECalType; 201 202 U_NAMESPACE_BEGIN 203 204 SharedCalendar::~SharedCalendar() { 205 delete ptr; 206 } 207 208 template<> U_I18N_API 209 const SharedCalendar *LocaleCacheKey<SharedCalendar>::createObject( 210 const void * /*unusedCreationContext*/, UErrorCode &status) const { 211 Calendar *calendar = Calendar::makeInstance(fLoc, status); 212 if (U_FAILURE(status)) { 213 return NULL; 214 } 215 SharedCalendar *shared = new SharedCalendar(calendar); 216 if (shared == NULL) { 217 delete calendar; 218 status = U_MEMORY_ALLOCATION_ERROR; 219 return NULL; 220 } 221 shared->addRef(); 222 return shared; 223 } 224 225 static ECalType getCalendarType(const char *s) { 226 for (int i = 0; gCalTypes[i] != NULL; i++) { 227 if (uprv_stricmp(s, gCalTypes[i]) == 0) { 228 return (ECalType)i; 229 } 230 } 231 return CALTYPE_UNKNOWN; 232 } 233 234 static UBool isStandardSupportedKeyword(const char *keyword, UErrorCode& status) { 235 if(U_FAILURE(status)) { 236 return FALSE; 237 } 238 ECalType calType = getCalendarType(keyword); 239 return (calType != CALTYPE_UNKNOWN); 240 } 241 242 static void getCalendarKeyword(const UnicodeString &id, char *targetBuffer, int32_t targetBufferSize) { 243 UnicodeString calendarKeyword = UNICODE_STRING_SIMPLE("calendar="); 244 int32_t calKeyLen = calendarKeyword.length(); 245 int32_t keyLen = 0; 246 247 int32_t keywordIdx = id.indexOf((UChar)0x003D); /* '=' */ 248 if (id[0] == 0x40/*'@'*/ 249 && id.compareBetween(1, keywordIdx+1, calendarKeyword, 0, calKeyLen) == 0) 250 { 251 keyLen = id.extract(keywordIdx+1, id.length(), targetBuffer, targetBufferSize, US_INV); 252 } 253 targetBuffer[keyLen] = 0; 254 } 255 256 static ECalType getCalendarTypeForLocale(const char *locid) { 257 UErrorCode status = U_ZERO_ERROR; 258 ECalType calType = CALTYPE_UNKNOWN; 259 260 //TODO: ULOC_FULL_NAME is out of date and too small.. 261 char canonicalName[256]; 262 263 // canonicalize, so grandfathered variant will be transformed to keywords 264 // e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese 265 int32_t canonicalLen = uloc_canonicalize(locid, canonicalName, sizeof(canonicalName) - 1, &status); 266 if (U_FAILURE(status)) { 267 return CALTYPE_GREGORIAN; 268 } 269 canonicalName[canonicalLen] = 0; // terminate 270 271 char calTypeBuf[32]; 272 int32_t calTypeBufLen; 273 274 calTypeBufLen = uloc_getKeywordValue(canonicalName, "calendar", calTypeBuf, sizeof(calTypeBuf) - 1, &status); 275 if (U_SUCCESS(status)) { 276 calTypeBuf[calTypeBufLen] = 0; 277 calType = getCalendarType(calTypeBuf); 278 if (calType != CALTYPE_UNKNOWN) { 279 return calType; 280 } 281 } 282 status = U_ZERO_ERROR; 283 284 // when calendar keyword is not available or not supported, read supplementalData 285 // to get the default calendar type for the locale's region 286 char region[ULOC_COUNTRY_CAPACITY]; 287 int32_t regionLen = 0; 288 regionLen = uloc_getCountry(canonicalName, region, sizeof(region) - 1, &status); 289 if (regionLen == 0) { 290 char fullLoc[256]; 291 uloc_addLikelySubtags(locid, fullLoc, sizeof(fullLoc) - 1, &status); 292 regionLen = uloc_getCountry(fullLoc, region, sizeof(region) - 1, &status); 293 } 294 if (U_FAILURE(status)) { 295 return CALTYPE_GREGORIAN; 296 } 297 region[regionLen] = 0; 298 299 // Read preferred calendar values from supplementalData calendarPreference 300 UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", &status); 301 ures_getByKey(rb, "calendarPreferenceData", rb, &status); 302 UResourceBundle *order = ures_getByKey(rb, region, NULL, &status); 303 if (status == U_MISSING_RESOURCE_ERROR && rb != NULL) { 304 status = U_ZERO_ERROR; 305 order = ures_getByKey(rb, "001", NULL, &status); 306 } 307 308 calTypeBuf[0] = 0; 309 if (U_SUCCESS(status) && order != NULL) { 310 // the first calender type is the default for the region 311 int32_t len = 0; 312 const UChar *uCalType = ures_getStringByIndex(order, 0, &len, &status); 313 if (len < (int32_t)sizeof(calTypeBuf)) { 314 u_UCharsToChars(uCalType, calTypeBuf, len); 315 *(calTypeBuf + len) = 0; // terminate; 316 calType = getCalendarType(calTypeBuf); 317 } 318 } 319 320 ures_close(order); 321 ures_close(rb); 322 323 if (calType == CALTYPE_UNKNOWN) { 324 // final fallback 325 calType = CALTYPE_GREGORIAN; 326 } 327 return calType; 328 } 329 330 static Calendar *createStandardCalendar(ECalType calType, const Locale &loc, UErrorCode& status) { 331 Calendar *cal = NULL; 332 333 switch (calType) { 334 case CALTYPE_GREGORIAN: 335 cal = new GregorianCalendar(loc, status); 336 break; 337 case CALTYPE_JAPANESE: 338 cal = new JapaneseCalendar(loc, status); 339 break; 340 case CALTYPE_BUDDHIST: 341 cal = new BuddhistCalendar(loc, status); 342 break; 343 case CALTYPE_ROC: 344 cal = new TaiwanCalendar(loc, status); 345 break; 346 case CALTYPE_PERSIAN: 347 cal = new PersianCalendar(loc, status); 348 break; 349 case CALTYPE_ISLAMIC_TBLA: 350 cal = new IslamicCalendar(loc, status, IslamicCalendar::TBLA); 351 break; 352 case CALTYPE_ISLAMIC_CIVIL: 353 cal = new IslamicCalendar(loc, status, IslamicCalendar::CIVIL); 354 break; 355 case CALTYPE_ISLAMIC_RGSA: 356 // default any region specific not handled individually to islamic 357 case CALTYPE_ISLAMIC: 358 cal = new IslamicCalendar(loc, status, IslamicCalendar::ASTRONOMICAL); 359 break; 360 case CALTYPE_ISLAMIC_UMALQURA: 361 cal = new IslamicCalendar(loc, status, IslamicCalendar::UMALQURA); 362 break; 363 case CALTYPE_HEBREW: 364 cal = new HebrewCalendar(loc, status); 365 break; 366 case CALTYPE_CHINESE: 367 cal = new ChineseCalendar(loc, status); 368 break; 369 case CALTYPE_INDIAN: 370 cal = new IndianCalendar(loc, status); 371 break; 372 case CALTYPE_COPTIC: 373 cal = new CopticCalendar(loc, status); 374 break; 375 case CALTYPE_ETHIOPIC: 376 cal = new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_MIHRET_ERA); 377 break; 378 case CALTYPE_ETHIOPIC_AMETE_ALEM: 379 cal = new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_ALEM_ERA); 380 break; 381 case CALTYPE_ISO8601: 382 cal = new GregorianCalendar(loc, status); 383 cal->setFirstDayOfWeek(UCAL_MONDAY); 384 cal->setMinimalDaysInFirstWeek(4); 385 break; 386 case CALTYPE_DANGI: 387 cal = new DangiCalendar(loc, status); 388 break; 389 default: 390 status = U_UNSUPPORTED_ERROR; 391 } 392 return cal; 393 } 394 395 396 #if !UCONFIG_NO_SERVICE 397 398 // ------------------------------------- 399 400 /** 401 * a Calendar Factory which creates the "basic" calendar types, that is, those 402 * shipped with ICU. 403 */ 404 class BasicCalendarFactory : public LocaleKeyFactory { 405 public: 406 /** 407 * @param calendarType static const string (caller owns storage - will be aliased) to calendar type 408 */ 409 BasicCalendarFactory() 410 : LocaleKeyFactory(LocaleKeyFactory::INVISIBLE) { } 411 412 virtual ~BasicCalendarFactory(); 413 414 protected: 415 //virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const { 416 // if(U_FAILURE(status)) { 417 // return FALSE; 418 // } 419 // char keyword[ULOC_FULLNAME_CAPACITY]; 420 // getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword)); 421 // return isStandardSupportedKeyword(keyword, status); 422 //} 423 424 virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const 425 { 426 if (U_SUCCESS(status)) { 427 for(int32_t i=0;gCalTypes[i] != NULL;i++) { 428 UnicodeString id((UChar)0x40); /* '@' a variant character */ 429 id.append(UNICODE_STRING_SIMPLE("calendar=")); 430 id.append(UnicodeString(gCalTypes[i], -1, US_INV)); 431 result.put(id, (void*)this, status); 432 } 433 } 434 } 435 436 virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const { 437 #ifdef U_DEBUG_CALSVC 438 if(dynamic_cast<const LocaleKey*>(&key) == NULL) { 439 fprintf(stderr, "::create - not a LocaleKey!\n"); 440 } 441 #endif 442 const LocaleKey& lkey = (LocaleKey&)key; 443 Locale curLoc; // current locale 444 Locale canLoc; // Canonical locale 445 446 lkey.currentLocale(curLoc); 447 lkey.canonicalLocale(canLoc); 448 449 char keyword[ULOC_FULLNAME_CAPACITY]; 450 UnicodeString str; 451 452 key.currentID(str); 453 getCalendarKeyword(str, keyword, (int32_t) sizeof(keyword)); 454 455 #ifdef U_DEBUG_CALSVC 456 fprintf(stderr, "BasicCalendarFactory::create() - cur %s, can %s\n", (const char*)curLoc.getName(), (const char*)canLoc.getName()); 457 #endif 458 459 if(!isStandardSupportedKeyword(keyword,status)) { // Do we handle this type? 460 #ifdef U_DEBUG_CALSVC 461 462 fprintf(stderr, "BasicCalendarFactory - not handling %s.[%s]\n", (const char*) curLoc.getName(), tmp ); 463 #endif 464 return NULL; 465 } 466 467 return createStandardCalendar(getCalendarType(keyword), canLoc, status); 468 } 469 }; 470 471 BasicCalendarFactory::~BasicCalendarFactory() {} 472 473 /** 474 * A factory which looks up the DefaultCalendar resource to determine which class of calendar to use 475 */ 476 477 class DefaultCalendarFactory : public ICUResourceBundleFactory { 478 public: 479 DefaultCalendarFactory() : ICUResourceBundleFactory() { } 480 virtual ~DefaultCalendarFactory(); 481 protected: 482 virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const { 483 484 LocaleKey &lkey = (LocaleKey&)key; 485 Locale loc; 486 lkey.currentLocale(loc); 487 488 UnicodeString *ret = new UnicodeString(); 489 if (ret == NULL) { 490 status = U_MEMORY_ALLOCATION_ERROR; 491 } else { 492 ret->append((UChar)0x40); // '@' is a variant character 493 ret->append(UNICODE_STRING("calendar=", 9)); 494 ret->append(UnicodeString(gCalTypes[getCalendarTypeForLocale(loc.getName())], -1, US_INV)); 495 } 496 return ret; 497 } 498 }; 499 500 DefaultCalendarFactory::~DefaultCalendarFactory() {} 501 502 // ------------------------------------- 503 class CalendarService : public ICULocaleService { 504 public: 505 CalendarService() 506 : ICULocaleService(UNICODE_STRING_SIMPLE("Calendar")) 507 { 508 UErrorCode status = U_ZERO_ERROR; 509 registerFactory(new DefaultCalendarFactory(), status); 510 } 511 512 virtual ~CalendarService(); 513 514 virtual UObject* cloneInstance(UObject* instance) const { 515 UnicodeString *s = dynamic_cast<UnicodeString *>(instance); 516 if(s != NULL) { 517 return s->clone(); 518 } else { 519 #ifdef U_DEBUG_CALSVC_F 520 UErrorCode status2 = U_ZERO_ERROR; 521 fprintf(stderr, "Cloning a %s calendar with tz=%ld\n", ((Calendar*)instance)->getType(), ((Calendar*)instance)->get(UCAL_ZONE_OFFSET, status2)); 522 #endif 523 return ((Calendar*)instance)->clone(); 524 } 525 } 526 527 virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /*actualID*/, UErrorCode& status) const { 528 LocaleKey& lkey = (LocaleKey&)key; 529 //int32_t kind = lkey.kind(); 530 531 Locale loc; 532 lkey.canonicalLocale(loc); 533 534 #ifdef U_DEBUG_CALSVC 535 Locale loc2; 536 lkey.currentLocale(loc2); 537 fprintf(stderr, "CalSvc:handleDefault for currentLoc %s, canloc %s\n", (const char*)loc.getName(), (const char*)loc2.getName()); 538 #endif 539 Calendar *nc = new GregorianCalendar(loc, status); 540 541 #ifdef U_DEBUG_CALSVC 542 UErrorCode status2 = U_ZERO_ERROR; 543 fprintf(stderr, "New default calendar has tz=%d\n", ((Calendar*)nc)->get(UCAL_ZONE_OFFSET, status2)); 544 #endif 545 return nc; 546 } 547 548 virtual UBool isDefault() const { 549 return countFactories() == 1; 550 } 551 }; 552 553 CalendarService::~CalendarService() {} 554 555 // ------------------------------------- 556 557 static inline UBool 558 isCalendarServiceUsed() { 559 return !gServiceInitOnce.isReset(); 560 } 561 562 // ------------------------------------- 563 564 static void U_CALLCONV 565 initCalendarService(UErrorCode &status) 566 { 567 #ifdef U_DEBUG_CALSVC 568 fprintf(stderr, "Spinning up Calendar Service\n"); 569 #endif 570 ucln_i18n_registerCleanup(UCLN_I18N_CALENDAR, calendar_cleanup); 571 gService = new CalendarService(); 572 if (gService == NULL) { 573 status = U_MEMORY_ALLOCATION_ERROR; 574 return; 575 } 576 #ifdef U_DEBUG_CALSVC 577 fprintf(stderr, "Registering classes..\n"); 578 #endif 579 580 // Register all basic instances. 581 gService->registerFactory(new BasicCalendarFactory(),status); 582 583 #ifdef U_DEBUG_CALSVC 584 fprintf(stderr, "Done..\n"); 585 #endif 586 587 if(U_FAILURE(status)) { 588 #ifdef U_DEBUG_CALSVC 589 fprintf(stderr, "err (%s) registering classes, deleting service.....\n", u_errorName(status)); 590 #endif 591 delete gService; 592 gService = NULL; 593 } 594 } 595 596 static ICULocaleService* 597 getCalendarService(UErrorCode &status) 598 { 599 umtx_initOnce(gServiceInitOnce, &initCalendarService, status); 600 return gService; 601 } 602 603 URegistryKey Calendar::registerFactory(ICUServiceFactory* toAdopt, UErrorCode& status) 604 { 605 return getCalendarService(status)->registerFactory(toAdopt, status); 606 } 607 608 UBool Calendar::unregister(URegistryKey key, UErrorCode& status) { 609 return getCalendarService(status)->unregister(key, status); 610 } 611 #endif /* UCONFIG_NO_SERVICE */ 612 613 // ------------------------------------- 614 615 static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = { 616 // Minimum Greatest min Least max Greatest max 617 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // ERA 618 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR 619 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // MONTH 620 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_YEAR 621 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_MONTH 622 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_MONTH 623 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_YEAR 624 { 1, 1, 7, 7 }, // DAY_OF_WEEK 625 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_WEEK_IN_MONTH 626 { 0, 0, 1, 1 }, // AM_PM 627 { 0, 0, 11, 11 }, // HOUR 628 { 0, 0, 23, 23 }, // HOUR_OF_DAY 629 { 0, 0, 59, 59 }, // MINUTE 630 { 0, 0, 59, 59 }, // SECOND 631 { 0, 0, 999, 999 }, // MILLISECOND 632 {-12*kOneHour, -12*kOneHour, 12*kOneHour, 15*kOneHour }, // ZONE_OFFSET 633 { 0, 0, 1*kOneHour, 1*kOneHour }, // DST_OFFSET 634 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR_WOY 635 { 1, 1, 7, 7 }, // DOW_LOCAL 636 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // EXTENDED_YEAR 637 { -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY 638 { 0, 0, 24*kOneHour-1, 24*kOneHour-1 }, // MILLISECONDS_IN_DAY 639 { 0, 0, 1, 1 }, // IS_LEAP_MONTH 640 }; 641 642 // Resource bundle tags read by this class 643 static const char gMonthNames[] = "monthNames"; 644 645 // Data flow in Calendar 646 // --------------------- 647 648 // The current time is represented in two ways by Calendar: as UTC 649 // milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local 650 // fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the 651 // millis from the fields, and vice versa. The data needed to do this 652 // conversion is encapsulated by a TimeZone object owned by the Calendar. 653 // The data provided by the TimeZone object may also be overridden if the 654 // user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class 655 // keeps track of what information was most recently set by the caller, and 656 // uses that to compute any other information as needed. 657 658 // If the user sets the fields using set(), the data flow is as follows. 659 // This is implemented by the Calendar subclass's computeTime() method. 660 // During this process, certain fields may be ignored. The disambiguation 661 // algorithm for resolving which fields to pay attention to is described 662 // above. 663 664 // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.) 665 // | 666 // | Using Calendar-specific algorithm 667 // V 668 // local standard millis 669 // | 670 // | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET 671 // V 672 // UTC millis (in time data member) 673 674 // If the user sets the UTC millis using setTime(), the data flow is as 675 // follows. This is implemented by the Calendar subclass's computeFields() 676 // method. 677 678 // UTC millis (in time data member) 679 // | 680 // | Using TimeZone getOffset() 681 // V 682 // local standard millis 683 // | 684 // | Using Calendar-specific algorithm 685 // V 686 // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.) 687 688 // In general, a round trip from fields, through local and UTC millis, and 689 // back out to fields is made when necessary. This is implemented by the 690 // complete() method. Resolving a partial set of fields into a UTC millis 691 // value allows all remaining fields to be generated from that value. If 692 // the Calendar is lenient, the fields are also renormalized to standard 693 // ranges when they are regenerated. 694 695 // ------------------------------------- 696 697 Calendar::Calendar(UErrorCode& success) 698 : UObject(), 699 fIsTimeSet(FALSE), 700 fAreFieldsSet(FALSE), 701 fAreAllFieldsSet(FALSE), 702 fAreFieldsVirtuallySet(FALSE), 703 fNextStamp((int32_t)kMinimumUserStamp), 704 fTime(0), 705 fLenient(TRUE), 706 fZone(NULL), 707 fRepeatedWallTime(UCAL_WALLTIME_LAST), 708 fSkippedWallTime(UCAL_WALLTIME_LAST) 709 { 710 clear(); 711 if (U_FAILURE(success)) { 712 return; 713 } 714 fZone = TimeZone::createDefault(); 715 if (fZone == NULL) { 716 success = U_MEMORY_ALLOCATION_ERROR; 717 } 718 setWeekData(Locale::getDefault(), NULL, success); 719 } 720 721 // ------------------------------------- 722 723 Calendar::Calendar(TimeZone* zone, const Locale& aLocale, UErrorCode& success) 724 : UObject(), 725 fIsTimeSet(FALSE), 726 fAreFieldsSet(FALSE), 727 fAreAllFieldsSet(FALSE), 728 fAreFieldsVirtuallySet(FALSE), 729 fNextStamp((int32_t)kMinimumUserStamp), 730 fTime(0), 731 fLenient(TRUE), 732 fZone(NULL), 733 fRepeatedWallTime(UCAL_WALLTIME_LAST), 734 fSkippedWallTime(UCAL_WALLTIME_LAST) 735 { 736 if (U_FAILURE(success)) { 737 return; 738 } 739 if(zone == 0) { 740 #if defined (U_DEBUG_CAL) 741 fprintf(stderr, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n", 742 __FILE__, __LINE__); 743 #endif 744 success = U_ILLEGAL_ARGUMENT_ERROR; 745 return; 746 } 747 748 clear(); 749 fZone = zone; 750 setWeekData(aLocale, NULL, success); 751 } 752 753 // ------------------------------------- 754 755 Calendar::Calendar(const TimeZone& zone, const Locale& aLocale, UErrorCode& success) 756 : UObject(), 757 fIsTimeSet(FALSE), 758 fAreFieldsSet(FALSE), 759 fAreAllFieldsSet(FALSE), 760 fAreFieldsVirtuallySet(FALSE), 761 fNextStamp((int32_t)kMinimumUserStamp), 762 fTime(0), 763 fLenient(TRUE), 764 fZone(NULL), 765 fRepeatedWallTime(UCAL_WALLTIME_LAST), 766 fSkippedWallTime(UCAL_WALLTIME_LAST) 767 { 768 if (U_FAILURE(success)) { 769 return; 770 } 771 clear(); 772 fZone = zone.clone(); 773 if (fZone == NULL) { 774 success = U_MEMORY_ALLOCATION_ERROR; 775 } 776 setWeekData(aLocale, NULL, success); 777 } 778 779 // ------------------------------------- 780 781 Calendar::~Calendar() 782 { 783 delete fZone; 784 } 785 786 // ------------------------------------- 787 788 Calendar::Calendar(const Calendar &source) 789 : UObject(source) 790 { 791 fZone = NULL; 792 *this = source; 793 } 794 795 // ------------------------------------- 796 797 Calendar & 798 Calendar::operator=(const Calendar &right) 799 { 800 if (this != &right) { 801 uprv_arrayCopy(right.fFields, fFields, UCAL_FIELD_COUNT); 802 uprv_arrayCopy(right.fIsSet, fIsSet, UCAL_FIELD_COUNT); 803 uprv_arrayCopy(right.fStamp, fStamp, UCAL_FIELD_COUNT); 804 fTime = right.fTime; 805 fIsTimeSet = right.fIsTimeSet; 806 fAreAllFieldsSet = right.fAreAllFieldsSet; 807 fAreFieldsSet = right.fAreFieldsSet; 808 fAreFieldsVirtuallySet = right.fAreFieldsVirtuallySet; 809 fLenient = right.fLenient; 810 fRepeatedWallTime = right.fRepeatedWallTime; 811 fSkippedWallTime = right.fSkippedWallTime; 812 delete fZone; 813 fZone = NULL; 814 if (right.fZone != NULL) { 815 fZone = right.fZone->clone(); 816 } 817 fFirstDayOfWeek = right.fFirstDayOfWeek; 818 fMinimalDaysInFirstWeek = right.fMinimalDaysInFirstWeek; 819 fWeekendOnset = right.fWeekendOnset; 820 fWeekendOnsetMillis = right.fWeekendOnsetMillis; 821 fWeekendCease = right.fWeekendCease; 822 fWeekendCeaseMillis = right.fWeekendCeaseMillis; 823 fNextStamp = right.fNextStamp; 824 uprv_strcpy(validLocale, right.validLocale); 825 uprv_strcpy(actualLocale, right.actualLocale); 826 } 827 828 return *this; 829 } 830 831 // ------------------------------------- 832 833 Calendar* U_EXPORT2 834 Calendar::createInstance(UErrorCode& success) 835 { 836 return createInstance(TimeZone::createDefault(), Locale::getDefault(), success); 837 } 838 839 // ------------------------------------- 840 841 Calendar* U_EXPORT2 842 Calendar::createInstance(const TimeZone& zone, UErrorCode& success) 843 { 844 return createInstance(zone, Locale::getDefault(), success); 845 } 846 847 // ------------------------------------- 848 849 Calendar* U_EXPORT2 850 Calendar::createInstance(const Locale& aLocale, UErrorCode& success) 851 { 852 return createInstance(TimeZone::createDefault(), aLocale, success); 853 } 854 855 // ------------------------------------- Adopting 856 857 // Note: this is the bottleneck that actually calls the service routines. 858 859 Calendar * U_EXPORT2 860 Calendar::makeInstance(const Locale& aLocale, UErrorCode& success) { 861 if (U_FAILURE(success)) { 862 return NULL; 863 } 864 865 Locale actualLoc; 866 UObject* u = NULL; 867 868 #if !UCONFIG_NO_SERVICE 869 if (isCalendarServiceUsed()) { 870 u = getCalendarService(success)->get(aLocale, LocaleKey::KIND_ANY, &actualLoc, success); 871 } 872 else 873 #endif 874 { 875 u = createStandardCalendar(getCalendarTypeForLocale(aLocale.getName()), aLocale, success); 876 } 877 Calendar* c = NULL; 878 879 if(U_FAILURE(success) || !u) { 880 if(U_SUCCESS(success)) { // Propagate some kind of err 881 success = U_INTERNAL_PROGRAM_ERROR; 882 } 883 return NULL; 884 } 885 886 #if !UCONFIG_NO_SERVICE 887 const UnicodeString* str = dynamic_cast<const UnicodeString*>(u); 888 if(str != NULL) { 889 // It's a unicode string telling us what type of calendar to load ("gregorian", etc) 890 // Create a Locale over this string 891 Locale l(""); 892 LocaleUtility::initLocaleFromName(*str, l); 893 894 #ifdef U_DEBUG_CALSVC 895 fprintf(stderr, "Calendar::createInstance(%s), looking up [%s]\n", aLocale.getName(), l.getName()); 896 #endif 897 898 Locale actualLoc2; 899 delete u; 900 u = NULL; 901 902 // Don't overwrite actualLoc, since the actual loc from this call 903 // may be something like "@calendar=gregorian" -- TODO investigate 904 // further... 905 c = (Calendar*)getCalendarService(success)->get(l, LocaleKey::KIND_ANY, &actualLoc2, success); 906 907 if(U_FAILURE(success) || !c) { 908 if(U_SUCCESS(success)) { 909 success = U_INTERNAL_PROGRAM_ERROR; // Propagate some err 910 } 911 return NULL; 912 } 913 914 str = dynamic_cast<const UnicodeString*>(c); 915 if(str != NULL) { 916 // recursed! Second lookup returned a UnicodeString. 917 // Perhaps DefaultCalendar{} was set to another locale. 918 #ifdef U_DEBUG_CALSVC 919 char tmp[200]; 920 // Extract a char* out of it.. 921 int32_t len = str->length(); 922 int32_t actLen = sizeof(tmp)-1; 923 if(len > actLen) { 924 len = actLen; 925 } 926 str->extract(0,len,tmp); 927 tmp[len]=0; 928 929 fprintf(stderr, "err - recursed, 2nd lookup was unistring %s\n", tmp); 930 #endif 931 success = U_MISSING_RESOURCE_ERROR; // requested a calendar type which could NOT be found. 932 delete c; 933 return NULL; 934 } 935 #ifdef U_DEBUG_CALSVC 936 fprintf(stderr, "%p: setting week count data to locale %s, actual locale %s\n", c, (const char*)aLocale.getName(), (const char *)actualLoc.getName()); 937 #endif 938 c->setWeekData(aLocale, c->getType(), success); // set the correct locale (this was an indirected calendar) 939 940 char keyword[ULOC_FULLNAME_CAPACITY]; 941 UErrorCode tmpStatus = U_ZERO_ERROR; 942 l.getKeywordValue("calendar", keyword, ULOC_FULLNAME_CAPACITY, tmpStatus); 943 if (U_SUCCESS(tmpStatus) && uprv_strcmp(keyword, "iso8601") == 0) { 944 c->setFirstDayOfWeek(UCAL_MONDAY); 945 c->setMinimalDaysInFirstWeek(4); 946 } 947 } 948 else 949 #endif /* UCONFIG_NO_SERVICE */ 950 { 951 // a calendar was returned - we assume the factory did the right thing. 952 c = (Calendar*)u; 953 } 954 955 return c; 956 } 957 958 Calendar* U_EXPORT2 959 Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success) 960 { 961 LocalPointer<TimeZone> zonePtr(zone); 962 const SharedCalendar *shared = NULL; 963 UnifiedCache::getByLocale(aLocale, shared, success); 964 if (U_FAILURE(success)) { 965 return NULL; 966 } 967 Calendar *c = (*shared)->clone(); 968 shared->removeRef(); 969 if (c == NULL) { 970 success = U_MEMORY_ALLOCATION_ERROR; 971 return NULL; 972 } 973 974 // Now, reset calendar to default state: 975 c->adoptTimeZone(zonePtr.orphan()); // Set the correct time zone 976 c->setTimeInMillis(getNow(), success); // let the new calendar have the current time. 977 978 return c; 979 } 980 981 // ------------------------------------- 982 983 Calendar* U_EXPORT2 984 Calendar::createInstance(const TimeZone& zone, const Locale& aLocale, UErrorCode& success) 985 { 986 Calendar* c = createInstance(aLocale, success); 987 if(U_SUCCESS(success) && c) { 988 c->setTimeZone(zone); 989 } 990 return c; 991 } 992 993 // ------------------------------------- 994 995 void U_EXPORT2 996 Calendar::getCalendarTypeFromLocale( 997 const Locale &aLocale, 998 char *typeBuffer, 999 int32_t typeBufferSize, 1000 UErrorCode &success) { 1001 const SharedCalendar *shared = NULL; 1002 UnifiedCache::getByLocale(aLocale, shared, success); 1003 if (U_FAILURE(success)) { 1004 return; 1005 } 1006 uprv_strncpy(typeBuffer, (*shared)->getType(), typeBufferSize); 1007 shared->removeRef(); 1008 if (typeBuffer[typeBufferSize - 1]) { 1009 success = U_BUFFER_OVERFLOW_ERROR; 1010 } 1011 } 1012 1013 UBool 1014 Calendar::operator==(const Calendar& that) const 1015 { 1016 UErrorCode status = U_ZERO_ERROR; 1017 return isEquivalentTo(that) && 1018 getTimeInMillis(status) == that.getTimeInMillis(status) && 1019 U_SUCCESS(status); 1020 } 1021 1022 UBool 1023 Calendar::isEquivalentTo(const Calendar& other) const 1024 { 1025 return typeid(*this) == typeid(other) && 1026 fLenient == other.fLenient && 1027 fRepeatedWallTime == other.fRepeatedWallTime && 1028 fSkippedWallTime == other.fSkippedWallTime && 1029 fFirstDayOfWeek == other.fFirstDayOfWeek && 1030 fMinimalDaysInFirstWeek == other.fMinimalDaysInFirstWeek && 1031 fWeekendOnset == other.fWeekendOnset && 1032 fWeekendOnsetMillis == other.fWeekendOnsetMillis && 1033 fWeekendCease == other.fWeekendCease && 1034 fWeekendCeaseMillis == other.fWeekendCeaseMillis && 1035 *fZone == *other.fZone; 1036 } 1037 1038 // ------------------------------------- 1039 1040 UBool 1041 Calendar::equals(const Calendar& when, UErrorCode& status) const 1042 { 1043 return (this == &when || 1044 getTime(status) == when.getTime(status)); 1045 } 1046 1047 // ------------------------------------- 1048 1049 UBool 1050 Calendar::before(const Calendar& when, UErrorCode& status) const 1051 { 1052 return (this != &when && 1053 getTimeInMillis(status) < when.getTimeInMillis(status)); 1054 } 1055 1056 // ------------------------------------- 1057 1058 UBool 1059 Calendar::after(const Calendar& when, UErrorCode& status) const 1060 { 1061 return (this != &when && 1062 getTimeInMillis(status) > when.getTimeInMillis(status)); 1063 } 1064 1065 // ------------------------------------- 1066 1067 1068 const Locale* U_EXPORT2 1069 Calendar::getAvailableLocales(int32_t& count) 1070 { 1071 return Locale::getAvailableLocales(count); 1072 } 1073 1074 // ------------------------------------- 1075 1076 StringEnumeration* U_EXPORT2 1077 Calendar::getKeywordValuesForLocale(const char* key, 1078 const Locale& locale, UBool commonlyUsed, UErrorCode& status) 1079 { 1080 // This is a wrapper over ucal_getKeywordValuesForLocale 1081 UEnumeration *uenum = ucal_getKeywordValuesForLocale(key, locale.getName(), 1082 commonlyUsed, &status); 1083 if (U_FAILURE(status)) { 1084 uenum_close(uenum); 1085 return NULL; 1086 } 1087 return new UStringEnumeration(uenum); 1088 } 1089 1090 // ------------------------------------- 1091 1092 UDate U_EXPORT2 1093 Calendar::getNow() 1094 { 1095 return uprv_getUTCtime(); // return as milliseconds 1096 } 1097 1098 // ------------------------------------- 1099 1100 /** 1101 * Gets this Calendar's current time as a long. 1102 * @return the current time as UTC milliseconds from the epoch. 1103 */ 1104 double 1105 Calendar::getTimeInMillis(UErrorCode& status) const 1106 { 1107 if(U_FAILURE(status)) 1108 return 0.0; 1109 1110 if ( ! fIsTimeSet) 1111 ((Calendar*)this)->updateTime(status); 1112 1113 /* Test for buffer overflows */ 1114 if(U_FAILURE(status)) { 1115 return 0.0; 1116 } 1117 return fTime; 1118 } 1119 1120 // ------------------------------------- 1121 1122 /** 1123 * Sets this Calendar's current time from the given long value. 1124 * A status of U_ILLEGAL_ARGUMENT_ERROR is set when millis is 1125 * outside the range permitted by a Calendar object when not in lenient mode. 1126 * when in lenient mode the out of range values are pinned to their respective min/max. 1127 * @param date the new time in UTC milliseconds from the epoch. 1128 */ 1129 void 1130 Calendar::setTimeInMillis( double millis, UErrorCode& status ) { 1131 if(U_FAILURE(status)) 1132 return; 1133 1134 if (millis > MAX_MILLIS) { 1135 if(isLenient()) { 1136 millis = MAX_MILLIS; 1137 } else { 1138 status = U_ILLEGAL_ARGUMENT_ERROR; 1139 return; 1140 } 1141 } else if (millis < MIN_MILLIS) { 1142 if(isLenient()) { 1143 millis = MIN_MILLIS; 1144 } else { 1145 status = U_ILLEGAL_ARGUMENT_ERROR; 1146 return; 1147 } 1148 } 1149 1150 fTime = millis; 1151 fAreFieldsSet = fAreAllFieldsSet = FALSE; 1152 fIsTimeSet = fAreFieldsVirtuallySet = TRUE; 1153 1154 for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) { 1155 fFields[i] = 0; 1156 fStamp[i] = kUnset; 1157 fIsSet[i] = FALSE; 1158 } 1159 1160 1161 } 1162 1163 // ------------------------------------- 1164 1165 int32_t 1166 Calendar::get(UCalendarDateFields field, UErrorCode& status) const 1167 { 1168 // field values are only computed when actually requested; for more on when computation 1169 // of various things happens, see the "data flow in Calendar" description at the top 1170 // of this file 1171 if (U_SUCCESS(status)) ((Calendar*)this)->complete(status); // Cast away const 1172 return U_SUCCESS(status) ? fFields[field] : 0; 1173 } 1174 1175 // ------------------------------------- 1176 1177 void 1178 Calendar::set(UCalendarDateFields field, int32_t value) 1179 { 1180 if (fAreFieldsVirtuallySet) { 1181 UErrorCode ec = U_ZERO_ERROR; 1182 computeFields(ec); 1183 } 1184 fFields[field] = value; 1185 /* Ensure that the fNextStamp value doesn't go pass max value for int32_t */ 1186 if (fNextStamp == STAMP_MAX) { 1187 recalculateStamp(); 1188 } 1189 fStamp[field] = fNextStamp++; 1190 fIsSet[field] = TRUE; // Remove later 1191 fIsTimeSet = fAreFieldsSet = fAreFieldsVirtuallySet = FALSE; 1192 } 1193 1194 // ------------------------------------- 1195 1196 void 1197 Calendar::set(int32_t year, int32_t month, int32_t date) 1198 { 1199 set(UCAL_YEAR, year); 1200 set(UCAL_MONTH, month); 1201 set(UCAL_DATE, date); 1202 } 1203 1204 // ------------------------------------- 1205 1206 void 1207 Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute) 1208 { 1209 set(UCAL_YEAR, year); 1210 set(UCAL_MONTH, month); 1211 set(UCAL_DATE, date); 1212 set(UCAL_HOUR_OF_DAY, hour); 1213 set(UCAL_MINUTE, minute); 1214 } 1215 1216 // ------------------------------------- 1217 1218 void 1219 Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute, int32_t second) 1220 { 1221 set(UCAL_YEAR, year); 1222 set(UCAL_MONTH, month); 1223 set(UCAL_DATE, date); 1224 set(UCAL_HOUR_OF_DAY, hour); 1225 set(UCAL_MINUTE, minute); 1226 set(UCAL_SECOND, second); 1227 } 1228 1229 // ------------------------------------- 1230 // For now the full getRelatedYear implementation is here; 1231 // per #10752 move the non-default implementation to subclasses 1232 // (default implementation will do no year adjustment) 1233 1234 static int32_t gregoYearFromIslamicStart(int32_t year) { 1235 // ad hoc conversion, improve under #10752 1236 // rough est for now, ok for grego 1846-2138, 1237 // otherwise occasionally wrong (for 3% of years) 1238 int cycle, offset, shift = 0; 1239 if (year >= 1397) { 1240 cycle = (year - 1397) / 67; 1241 offset = (year - 1397) % 67; 1242 shift = 2*cycle + ((offset >= 33)? 1: 0); 1243 } else { 1244 cycle = (year - 1396) / 67 - 1; 1245 offset = -(year - 1396) % 67; 1246 shift = 2*cycle + ((offset <= 33)? 1: 0); 1247 } 1248 return year + 579 - shift; 1249 } 1250 1251 int32_t Calendar::getRelatedYear(UErrorCode &status) const 1252 { 1253 if (U_FAILURE(status)) { 1254 return 0; 1255 } 1256 int32_t year = get(UCAL_EXTENDED_YEAR, status); 1257 if (U_FAILURE(status)) { 1258 return 0; 1259 } 1260 // modify for calendar type 1261 ECalType type = getCalendarType(getType()); 1262 switch (type) { 1263 case CALTYPE_PERSIAN: 1264 year += 622; break; 1265 case CALTYPE_HEBREW: 1266 year -= 3760; break; 1267 case CALTYPE_CHINESE: 1268 year -= 2637; break; 1269 case CALTYPE_INDIAN: 1270 year += 79; break; 1271 case CALTYPE_COPTIC: 1272 year += 284; break; 1273 case CALTYPE_ETHIOPIC: 1274 year += 8; break; 1275 case CALTYPE_ETHIOPIC_AMETE_ALEM: 1276 year -=5492; break; 1277 case CALTYPE_DANGI: 1278 year -= 2333; break; 1279 case CALTYPE_ISLAMIC_CIVIL: 1280 case CALTYPE_ISLAMIC: 1281 case CALTYPE_ISLAMIC_UMALQURA: 1282 case CALTYPE_ISLAMIC_TBLA: 1283 case CALTYPE_ISLAMIC_RGSA: 1284 year = gregoYearFromIslamicStart(year); break; 1285 default: 1286 // CALTYPE_GREGORIAN 1287 // CALTYPE_JAPANESE 1288 // CALTYPE_BUDDHIST 1289 // CALTYPE_ROC 1290 // CALTYPE_ISO8601 1291 // do nothing, EXTENDED_YEAR same as Gregorian 1292 break; 1293 } 1294 return year; 1295 } 1296 1297 // ------------------------------------- 1298 // For now the full setRelatedYear implementation is here; 1299 // per #10752 move the non-default implementation to subclasses 1300 // (default implementation will do no year adjustment) 1301 1302 static int32_t firstIslamicStartYearFromGrego(int32_t year) { 1303 // ad hoc conversion, improve under #10752 1304 // rough est for now, ok for grego 1846-2138, 1305 // otherwise occasionally wrong (for 3% of years) 1306 int cycle, offset, shift = 0; 1307 if (year >= 1977) { 1308 cycle = (year - 1977) / 65; 1309 offset = (year - 1977) % 65; 1310 shift = 2*cycle + ((offset >= 32)? 1: 0); 1311 } else { 1312 cycle = (year - 1976) / 65 - 1; 1313 offset = -(year - 1976) % 65; 1314 shift = 2*cycle + ((offset <= 32)? 1: 0); 1315 } 1316 return year - 579 + shift; 1317 } 1318 void Calendar::setRelatedYear(int32_t year) 1319 { 1320 // modify for calendar type 1321 ECalType type = getCalendarType(getType()); 1322 switch (type) { 1323 case CALTYPE_PERSIAN: 1324 year -= 622; break; 1325 case CALTYPE_HEBREW: 1326 year += 3760; break; 1327 case CALTYPE_CHINESE: 1328 year += 2637; break; 1329 case CALTYPE_INDIAN: 1330 year -= 79; break; 1331 case CALTYPE_COPTIC: 1332 year -= 284; break; 1333 case CALTYPE_ETHIOPIC: 1334 year -= 8; break; 1335 case CALTYPE_ETHIOPIC_AMETE_ALEM: 1336 year +=5492; break; 1337 case CALTYPE_DANGI: 1338 year += 2333; break; 1339 case CALTYPE_ISLAMIC_CIVIL: 1340 case CALTYPE_ISLAMIC: 1341 case CALTYPE_ISLAMIC_UMALQURA: 1342 case CALTYPE_ISLAMIC_TBLA: 1343 case CALTYPE_ISLAMIC_RGSA: 1344 year = firstIslamicStartYearFromGrego(year); break; 1345 default: 1346 // CALTYPE_GREGORIAN 1347 // CALTYPE_JAPANESE 1348 // CALTYPE_BUDDHIST 1349 // CALTYPE_ROC 1350 // CALTYPE_ISO8601 1351 // do nothing, EXTENDED_YEAR same as Gregorian 1352 break; 1353 } 1354 // set extended year 1355 set(UCAL_EXTENDED_YEAR, year); 1356 } 1357 1358 // ------------------------------------- 1359 1360 void 1361 Calendar::clear() 1362 { 1363 for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) { 1364 fFields[i] = 0; // Must do this; other code depends on it 1365 fStamp[i] = kUnset; 1366 fIsSet[i] = FALSE; // Remove later 1367 } 1368 fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE; 1369 // fTime is not 'cleared' - may be used if no fields are set. 1370 } 1371 1372 // ------------------------------------- 1373 1374 void 1375 Calendar::clear(UCalendarDateFields field) 1376 { 1377 if (fAreFieldsVirtuallySet) { 1378 UErrorCode ec = U_ZERO_ERROR; 1379 computeFields(ec); 1380 } 1381 fFields[field] = 0; 1382 fStamp[field] = kUnset; 1383 fIsSet[field] = FALSE; // Remove later 1384 fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE; 1385 } 1386 1387 // ------------------------------------- 1388 1389 UBool 1390 Calendar::isSet(UCalendarDateFields field) const 1391 { 1392 return fAreFieldsVirtuallySet || (fStamp[field] != kUnset); 1393 } 1394 1395 1396 int32_t Calendar::newestStamp(UCalendarDateFields first, UCalendarDateFields last, int32_t bestStampSoFar) const 1397 { 1398 int32_t bestStamp = bestStampSoFar; 1399 for (int32_t i=(int32_t)first; i<=(int32_t)last; ++i) { 1400 if (fStamp[i] > bestStamp) { 1401 bestStamp = fStamp[i]; 1402 } 1403 } 1404 return bestStamp; 1405 } 1406 1407 1408 // ------------------------------------- 1409 1410 void 1411 Calendar::complete(UErrorCode& status) 1412 { 1413 if (!fIsTimeSet) { 1414 updateTime(status); 1415 /* Test for buffer overflows */ 1416 if(U_FAILURE(status)) { 1417 return; 1418 } 1419 } 1420 if (!fAreFieldsSet) { 1421 computeFields(status); // fills in unset fields 1422 /* Test for buffer overflows */ 1423 if(U_FAILURE(status)) { 1424 return; 1425 } 1426 fAreFieldsSet = TRUE; 1427 fAreAllFieldsSet = TRUE; 1428 } 1429 } 1430 1431 //------------------------------------------------------------------------- 1432 // Protected utility methods for use by subclasses. These are very handy 1433 // for implementing add, roll, and computeFields. 1434 //------------------------------------------------------------------------- 1435 1436 /** 1437 * Adjust the specified field so that it is within 1438 * the allowable range for the date to which this calendar is set. 1439 * For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH} 1440 * field for a calendar set to April 31 would cause it to be set 1441 * to April 30. 1442 * <p> 1443 * <b>Subclassing:</b> 1444 * <br> 1445 * This utility method is intended for use by subclasses that need to implement 1446 * their own overrides of {@link #roll roll} and {@link #add add}. 1447 * <p> 1448 * <b>Note:</b> 1449 * <code>pinField</code> is implemented in terms of 1450 * {@link #getActualMinimum getActualMinimum} 1451 * and {@link #getActualMaximum getActualMaximum}. If either of those methods uses 1452 * a slow, iterative algorithm for a particular field, it would be 1453 * unwise to attempt to call <code>pinField</code> for that field. If you 1454 * really do need to do so, you should override this method to do 1455 * something more efficient for that field. 1456 * <p> 1457 * @param field The calendar field whose value should be pinned. 1458 * 1459 * @see #getActualMinimum 1460 * @see #getActualMaximum 1461 * @stable ICU 2.0 1462 */ 1463 void Calendar::pinField(UCalendarDateFields field, UErrorCode& status) { 1464 int32_t max = getActualMaximum(field, status); 1465 int32_t min = getActualMinimum(field, status); 1466 1467 if (fFields[field] > max) { 1468 set(field, max); 1469 } else if (fFields[field] < min) { 1470 set(field, min); 1471 } 1472 } 1473 1474 1475 void Calendar::computeFields(UErrorCode &ec) 1476 { 1477 if (U_FAILURE(ec)) { 1478 return; 1479 } 1480 // Compute local wall millis 1481 double localMillis = internalGetTime(); 1482 int32_t rawOffset, dstOffset; 1483 getTimeZone().getOffset(localMillis, FALSE, rawOffset, dstOffset, ec); 1484 localMillis += (rawOffset + dstOffset); 1485 1486 // Mark fields as set. Do this before calling handleComputeFields(). 1487 uint32_t mask = //fInternalSetMask; 1488 (1 << UCAL_ERA) | 1489 (1 << UCAL_YEAR) | 1490 (1 << UCAL_MONTH) | 1491 (1 << UCAL_DAY_OF_MONTH) | // = UCAL_DATE 1492 (1 << UCAL_DAY_OF_YEAR) | 1493 (1 << UCAL_EXTENDED_YEAR); 1494 1495 for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) { 1496 if ((mask & 1) == 0) { 1497 fStamp[i] = kInternallySet; 1498 fIsSet[i] = TRUE; // Remove later 1499 } else { 1500 fStamp[i] = kUnset; 1501 fIsSet[i] = FALSE; // Remove later 1502 } 1503 mask >>= 1; 1504 } 1505 1506 // We used to check for and correct extreme millis values (near 1507 // Long.MIN_VALUE or Long.MAX_VALUE) here. Such values would cause 1508 // overflows from positive to negative (or vice versa) and had to 1509 // be manually tweaked. We no longer need to do this because we 1510 // have limited the range of supported dates to those that have a 1511 // Julian day that fits into an int. This allows us to implement a 1512 // JULIAN_DAY field and also removes some inelegant code. - Liu 1513 // 11/6/00 1514 1515 int32_t days = (int32_t)ClockMath::floorDivide(localMillis, (double)kOneDay); 1516 1517 internalSet(UCAL_JULIAN_DAY,days + kEpochStartAsJulianDay); 1518 1519 #if defined (U_DEBUG_CAL) 1520 //fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n", 1521 //__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis); 1522 #endif 1523 1524 computeGregorianAndDOWFields(fFields[UCAL_JULIAN_DAY], ec); 1525 1526 // Call framework method to have subclass compute its fields. 1527 // These must include, at a minimum, MONTH, DAY_OF_MONTH, 1528 // EXTENDED_YEAR, YEAR, DAY_OF_YEAR. This method will call internalSet(), 1529 // which will update stamp[]. 1530 handleComputeFields(fFields[UCAL_JULIAN_DAY], ec); 1531 1532 // Compute week-related fields, based on the subclass-computed 1533 // fields computed by handleComputeFields(). 1534 computeWeekFields(ec); 1535 1536 // Compute time-related fields. These are indepent of the date and 1537 // of the subclass algorithm. They depend only on the local zone 1538 // wall milliseconds in day. 1539 int32_t millisInDay = (int32_t) (localMillis - (days * kOneDay)); 1540 fFields[UCAL_MILLISECONDS_IN_DAY] = millisInDay; 1541 fFields[UCAL_MILLISECOND] = millisInDay % 1000; 1542 millisInDay /= 1000; 1543 fFields[UCAL_SECOND] = millisInDay % 60; 1544 millisInDay /= 60; 1545 fFields[UCAL_MINUTE] = millisInDay % 60; 1546 millisInDay /= 60; 1547 fFields[UCAL_HOUR_OF_DAY] = millisInDay; 1548 fFields[UCAL_AM_PM] = millisInDay / 12; // Assume AM == 0 1549 fFields[UCAL_HOUR] = millisInDay % 12; 1550 fFields[UCAL_ZONE_OFFSET] = rawOffset; 1551 fFields[UCAL_DST_OFFSET] = dstOffset; 1552 } 1553 1554 uint8_t Calendar::julianDayToDayOfWeek(double julian) 1555 { 1556 // If julian is negative, then julian%7 will be negative, so we adjust 1557 // accordingly. We add 1 because Julian day 0 is Monday. 1558 int8_t dayOfWeek = (int8_t) uprv_fmod(julian + 1, 7); 1559 1560 uint8_t result = (uint8_t)(dayOfWeek + ((dayOfWeek < 0) ? (7+UCAL_SUNDAY ) : UCAL_SUNDAY)); 1561 return result; 1562 } 1563 1564 /** 1565 * Compute the Gregorian calendar year, month, and day of month from 1566 * the given Julian day. These values are not stored in fields, but in 1567 * member variables gregorianXxx. Also compute the DAY_OF_WEEK and 1568 * DOW_LOCAL fields. 1569 */ 1570 void Calendar::computeGregorianAndDOWFields(int32_t julianDay, UErrorCode &ec) 1571 { 1572 computeGregorianFields(julianDay, ec); 1573 1574 // Compute day of week: JD 0 = Monday 1575 int32_t dow = julianDayToDayOfWeek(julianDay); 1576 internalSet(UCAL_DAY_OF_WEEK,dow); 1577 1578 // Calculate 1-based localized day of week 1579 int32_t dowLocal = dow - getFirstDayOfWeek() + 1; 1580 if (dowLocal < 1) { 1581 dowLocal += 7; 1582 } 1583 internalSet(UCAL_DOW_LOCAL,dowLocal); 1584 fFields[UCAL_DOW_LOCAL] = dowLocal; 1585 } 1586 1587 /** 1588 * Compute the Gregorian calendar year, month, and day of month from the 1589 * Julian day. These values are not stored in fields, but in member 1590 * variables gregorianXxx. They are used for time zone computations and by 1591 * subclasses that are Gregorian derivatives. Subclasses may call this 1592 * method to perform a Gregorian calendar millis->fields computation. 1593 */ 1594 void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode & /* ec */) { 1595 int32_t gregorianDayOfWeekUnused; 1596 Grego::dayToFields(julianDay - kEpochStartAsJulianDay, fGregorianYear, fGregorianMonth, fGregorianDayOfMonth, gregorianDayOfWeekUnused, fGregorianDayOfYear); 1597 } 1598 1599 /** 1600 * Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH, 1601 * DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR, 1602 * DAY_OF_WEEK, and DAY_OF_YEAR. The latter fields are computed by the 1603 * subclass based on the calendar system. 1604 * 1605 * <p>The YEAR_WOY field is computed simplistically. It is equal to YEAR 1606 * most of the time, but at the year boundary it may be adjusted to YEAR-1 1607 * or YEAR+1 to reflect the overlap of a week into an adjacent year. In 1608 * this case, a simple increment or decrement is performed on YEAR, even 1609 * though this may yield an invalid YEAR value. For instance, if the YEAR 1610 * is part of a calendar system with an N-year cycle field CYCLE, then 1611 * incrementing the YEAR may involve incrementing CYCLE and setting YEAR 1612 * back to 0 or 1. This is not handled by this code, and in fact cannot be 1613 * simply handled without having subclasses define an entire parallel set of 1614 * fields for fields larger than or equal to a year. This additional 1615 * complexity is not warranted, since the intention of the YEAR_WOY field is 1616 * to support ISO 8601 notation, so it will typically be used with a 1617 * proleptic Gregorian calendar, which has no field larger than a year. 1618 */ 1619 void Calendar::computeWeekFields(UErrorCode &ec) { 1620 if(U_FAILURE(ec)) { 1621 return; 1622 } 1623 int32_t eyear = fFields[UCAL_EXTENDED_YEAR]; 1624 int32_t dayOfWeek = fFields[UCAL_DAY_OF_WEEK]; 1625 int32_t dayOfYear = fFields[UCAL_DAY_OF_YEAR]; 1626 1627 // WEEK_OF_YEAR start 1628 // Compute the week of the year. For the Gregorian calendar, valid week 1629 // numbers run from 1 to 52 or 53, depending on the year, the first day 1630 // of the week, and the minimal days in the first week. For other 1631 // calendars, the valid range may be different -- it depends on the year 1632 // length. Days at the start of the year may fall into the last week of 1633 // the previous year; days at the end of the year may fall into the 1634 // first week of the next year. ASSUME that the year length is less than 1635 // 7000 days. 1636 int32_t yearOfWeekOfYear = eyear; 1637 int32_t relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; // 0..6 1638 int32_t relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; // 0..6 1639 int32_t woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53 1640 if ((7 - relDowJan1) >= getMinimalDaysInFirstWeek()) { 1641 ++woy; 1642 } 1643 1644 // Adjust for weeks at the year end that overlap into the previous or 1645 // next calendar year. 1646 if (woy == 0) { 1647 // We are the last week of the previous year. 1648 // Check to see if we are in the last week; if so, we need 1649 // to handle the case in which we are the first week of the 1650 // next year. 1651 1652 int32_t prevDoy = dayOfYear + handleGetYearLength(eyear - 1); 1653 woy = weekNumber(prevDoy, dayOfWeek); 1654 yearOfWeekOfYear--; 1655 } else { 1656 int32_t lastDoy = handleGetYearLength(eyear); 1657 // Fast check: For it to be week 1 of the next year, the DOY 1658 // must be on or after L-5, where L is yearLength(), then it 1659 // cannot possibly be week 1 of the next year: 1660 // L-5 L 1661 // doy: 359 360 361 362 363 364 365 001 1662 // dow: 1 2 3 4 5 6 7 1663 if (dayOfYear >= (lastDoy - 5)) { 1664 int32_t lastRelDow = (relDow + lastDoy - dayOfYear) % 7; 1665 if (lastRelDow < 0) { 1666 lastRelDow += 7; 1667 } 1668 if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) && 1669 ((dayOfYear + 7 - relDow) > lastDoy)) { 1670 woy = 1; 1671 yearOfWeekOfYear++; 1672 } 1673 } 1674 } 1675 fFields[UCAL_WEEK_OF_YEAR] = woy; 1676 fFields[UCAL_YEAR_WOY] = yearOfWeekOfYear; 1677 // WEEK_OF_YEAR end 1678 1679 int32_t dayOfMonth = fFields[UCAL_DAY_OF_MONTH]; 1680 fFields[UCAL_WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek); 1681 fFields[UCAL_DAY_OF_WEEK_IN_MONTH] = (dayOfMonth-1) / 7 + 1; 1682 #if defined (U_DEBUG_CAL) 1683 if(fFields[UCAL_DAY_OF_WEEK_IN_MONTH]==0) fprintf(stderr, "%s:%d: DOWIM %d on %g\n", 1684 __FILE__, __LINE__,fFields[UCAL_DAY_OF_WEEK_IN_MONTH], fTime); 1685 #endif 1686 } 1687 1688 1689 int32_t Calendar::weekNumber(int32_t desiredDay, int32_t dayOfPeriod, int32_t dayOfWeek) 1690 { 1691 // Determine the day of the week of the first day of the period 1692 // in question (either a year or a month). Zero represents the 1693 // first day of the week on this calendar. 1694 int32_t periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % 7; 1695 if (periodStartDayOfWeek < 0) periodStartDayOfWeek += 7; 1696 1697 // Compute the week number. Initially, ignore the first week, which 1698 // may be fractional (or may not be). We add periodStartDayOfWeek in 1699 // order to fill out the first week, if it is fractional. 1700 int32_t weekNo = (desiredDay + periodStartDayOfWeek - 1)/7; 1701 1702 // If the first week is long enough, then count it. If 1703 // the minimal days in the first week is one, or if the period start 1704 // is zero, we always increment weekNo. 1705 if ((7 - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo; 1706 1707 return weekNo; 1708 } 1709 1710 void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode &/* status */) 1711 { 1712 internalSet(UCAL_MONTH, getGregorianMonth()); 1713 internalSet(UCAL_DAY_OF_MONTH, getGregorianDayOfMonth()); 1714 internalSet(UCAL_DAY_OF_YEAR, getGregorianDayOfYear()); 1715 int32_t eyear = getGregorianYear(); 1716 internalSet(UCAL_EXTENDED_YEAR, eyear); 1717 int32_t era = GregorianCalendar::AD; 1718 if (eyear < 1) { 1719 era = GregorianCalendar::BC; 1720 eyear = 1 - eyear; 1721 } 1722 internalSet(UCAL_ERA, era); 1723 internalSet(UCAL_YEAR, eyear); 1724 } 1725 // ------------------------------------- 1726 1727 1728 void Calendar::roll(EDateFields field, int32_t amount, UErrorCode& status) 1729 { 1730 roll((UCalendarDateFields)field, amount, status); 1731 } 1732 1733 void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) 1734 { 1735 if (amount == 0) { 1736 return; // Nothing to do 1737 } 1738 1739 complete(status); 1740 1741 if(U_FAILURE(status)) { 1742 return; 1743 } 1744 switch (field) { 1745 case UCAL_DAY_OF_MONTH: 1746 case UCAL_AM_PM: 1747 case UCAL_MINUTE: 1748 case UCAL_SECOND: 1749 case UCAL_MILLISECOND: 1750 case UCAL_MILLISECONDS_IN_DAY: 1751 case UCAL_ERA: 1752 // These are the standard roll instructions. These work for all 1753 // simple cases, that is, cases in which the limits are fixed, such 1754 // as the hour, the day of the month, and the era. 1755 { 1756 int32_t min = getActualMinimum(field,status); 1757 int32_t max = getActualMaximum(field,status); 1758 int32_t gap = max - min + 1; 1759 1760 int32_t value = internalGet(field) + amount; 1761 value = (value - min) % gap; 1762 if (value < 0) { 1763 value += gap; 1764 } 1765 value += min; 1766 1767 set(field, value); 1768 return; 1769 } 1770 1771 case UCAL_HOUR: 1772 case UCAL_HOUR_OF_DAY: 1773 // Rolling the hour is difficult on the ONSET and CEASE days of 1774 // daylight savings. For example, if the change occurs at 1775 // 2 AM, we have the following progression: 1776 // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst 1777 // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std 1778 // To get around this problem we don't use fields; we manipulate 1779 // the time in millis directly. 1780 { 1781 // Assume min == 0 in calculations below 1782 double start = getTimeInMillis(status); 1783 int32_t oldHour = internalGet(field); 1784 int32_t max = getMaximum(field); 1785 int32_t newHour = (oldHour + amount) % (max + 1); 1786 if (newHour < 0) { 1787 newHour += max + 1; 1788 } 1789 setTimeInMillis(start + kOneHour * (newHour - oldHour),status); 1790 return; 1791 } 1792 1793 case UCAL_MONTH: 1794 // Rolling the month involves both pinning the final value 1795 // and adjusting the DAY_OF_MONTH if necessary. We only adjust the 1796 // DAY_OF_MONTH if, after updating the MONTH field, it is illegal. 1797 // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>. 1798 { 1799 int32_t max = getActualMaximum(UCAL_MONTH, status); 1800 int32_t mon = (internalGet(UCAL_MONTH) + amount) % (max+1); 1801 1802 if (mon < 0) { 1803 mon += (max + 1); 1804 } 1805 set(UCAL_MONTH, mon); 1806 1807 // Keep the day of month in range. We don't want to spill over 1808 // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 -> 1809 // mar3. 1810 pinField(UCAL_DAY_OF_MONTH,status); 1811 return; 1812 } 1813 1814 case UCAL_YEAR: 1815 case UCAL_YEAR_WOY: 1816 { 1817 // * If era==0 and years go backwards in time, change sign of amount. 1818 // * Until we have new API per #9393, we temporarily hardcode knowledge of 1819 // which calendars have era 0 years that go backwards. 1820 UBool era0WithYearsThatGoBackwards = FALSE; 1821 int32_t era = get(UCAL_ERA, status); 1822 if (era == 0) { 1823 const char * calType = getType(); 1824 if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) { 1825 amount = -amount; 1826 era0WithYearsThatGoBackwards = TRUE; 1827 } 1828 } 1829 int32_t newYear = internalGet(field) + amount; 1830 if (era > 0 || newYear >= 1) { 1831 int32_t maxYear = getActualMaximum(field, status); 1832 if (maxYear < 32768) { 1833 // this era has real bounds, roll should wrap years 1834 if (newYear < 1) { 1835 newYear = maxYear - ((-newYear) % maxYear); 1836 } else if (newYear > maxYear) { 1837 newYear = ((newYear - 1) % maxYear) + 1; 1838 } 1839 // else era is unbounded, just pin low year instead of wrapping 1840 } else if (newYear < 1) { 1841 newYear = 1; 1842 } 1843 // else we are in era 0 with newYear < 1; 1844 // calendars with years that go backwards must pin the year value at 0, 1845 // other calendars can have years < 0 in era 0 1846 } else if (era0WithYearsThatGoBackwards) { 1847 newYear = 1; 1848 } 1849 set(field, newYear); 1850 pinField(UCAL_MONTH,status); 1851 pinField(UCAL_DAY_OF_MONTH,status); 1852 return; 1853 } 1854 1855 case UCAL_EXTENDED_YEAR: 1856 // Rolling the year can involve pinning the DAY_OF_MONTH. 1857 set(field, internalGet(field) + amount); 1858 pinField(UCAL_MONTH,status); 1859 pinField(UCAL_DAY_OF_MONTH,status); 1860 return; 1861 1862 case UCAL_WEEK_OF_MONTH: 1863 { 1864 // This is tricky, because during the roll we may have to shift 1865 // to a different day of the week. For example: 1866 1867 // s m t w r f s 1868 // 1 2 3 4 5 1869 // 6 7 8 9 10 11 12 1870 1871 // When rolling from the 6th or 7th back one week, we go to the 1872 // 1st (assuming that the first partial week counts). The same 1873 // thing happens at the end of the month. 1874 1875 // The other tricky thing is that we have to figure out whether 1876 // the first partial week actually counts or not, based on the 1877 // minimal first days in the week. And we have to use the 1878 // correct first day of the week to delineate the week 1879 // boundaries. 1880 1881 // Here's our algorithm. First, we find the real boundaries of 1882 // the month. Then we discard the first partial week if it 1883 // doesn't count in this locale. Then we fill in the ends with 1884 // phantom days, so that the first partial week and the last 1885 // partial week are full weeks. We then have a nice square 1886 // block of weeks. We do the usual rolling within this block, 1887 // as is done elsewhere in this method. If we wind up on one of 1888 // the phantom days that we added, we recognize this and pin to 1889 // the first or the last day of the month. Easy, eh? 1890 1891 // Normalize the DAY_OF_WEEK so that 0 is the first day of the week 1892 // in this locale. We have dow in 0..6. 1893 int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); 1894 if (dow < 0) dow += 7; 1895 1896 // Find the day of the week (normalized for locale) for the first 1897 // of the month. 1898 int32_t fdm = (dow - internalGet(UCAL_DAY_OF_MONTH) + 1) % 7; 1899 if (fdm < 0) fdm += 7; 1900 1901 // Get the first day of the first full week of the month, 1902 // including phantom days, if any. Figure out if the first week 1903 // counts or not; if it counts, then fill in phantom days. If 1904 // not, advance to the first real full week (skip the partial week). 1905 int32_t start; 1906 if ((7 - fdm) < getMinimalDaysInFirstWeek()) 1907 start = 8 - fdm; // Skip the first partial week 1908 else 1909 start = 1 - fdm; // This may be zero or negative 1910 1911 // Get the day of the week (normalized for locale) for the last 1912 // day of the month. 1913 int32_t monthLen = getActualMaximum(UCAL_DAY_OF_MONTH, status); 1914 int32_t ldm = (monthLen - internalGet(UCAL_DAY_OF_MONTH) + dow) % 7; 1915 // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here. 1916 1917 // Get the limit day for the blocked-off rectangular month; that 1918 // is, the day which is one past the last day of the month, 1919 // after the month has already been filled in with phantom days 1920 // to fill out the last week. This day has a normalized DOW of 0. 1921 int32_t limit = monthLen + 7 - ldm; 1922 1923 // Now roll between start and (limit - 1). 1924 int32_t gap = limit - start; 1925 int32_t day_of_month = (internalGet(UCAL_DAY_OF_MONTH) + amount*7 - 1926 start) % gap; 1927 if (day_of_month < 0) day_of_month += gap; 1928 day_of_month += start; 1929 1930 // Finally, pin to the real start and end of the month. 1931 if (day_of_month < 1) day_of_month = 1; 1932 if (day_of_month > monthLen) day_of_month = monthLen; 1933 1934 // Set the DAY_OF_MONTH. We rely on the fact that this field 1935 // takes precedence over everything else (since all other fields 1936 // are also set at this point). If this fact changes (if the 1937 // disambiguation algorithm changes) then we will have to unset 1938 // the appropriate fields here so that DAY_OF_MONTH is attended 1939 // to. 1940 set(UCAL_DAY_OF_MONTH, day_of_month); 1941 return; 1942 } 1943 case UCAL_WEEK_OF_YEAR: 1944 { 1945 // This follows the outline of WEEK_OF_MONTH, except it applies 1946 // to the whole year. Please see the comment for WEEK_OF_MONTH 1947 // for general notes. 1948 1949 // Normalize the DAY_OF_WEEK so that 0 is the first day of the week 1950 // in this locale. We have dow in 0..6. 1951 int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); 1952 if (dow < 0) dow += 7; 1953 1954 // Find the day of the week (normalized for locale) for the first 1955 // of the year. 1956 int32_t fdy = (dow - internalGet(UCAL_DAY_OF_YEAR) + 1) % 7; 1957 if (fdy < 0) fdy += 7; 1958 1959 // Get the first day of the first full week of the year, 1960 // including phantom days, if any. Figure out if the first week 1961 // counts or not; if it counts, then fill in phantom days. If 1962 // not, advance to the first real full week (skip the partial week). 1963 int32_t start; 1964 if ((7 - fdy) < getMinimalDaysInFirstWeek()) 1965 start = 8 - fdy; // Skip the first partial week 1966 else 1967 start = 1 - fdy; // This may be zero or negative 1968 1969 // Get the day of the week (normalized for locale) for the last 1970 // day of the year. 1971 int32_t yearLen = getActualMaximum(UCAL_DAY_OF_YEAR,status); 1972 int32_t ldy = (yearLen - internalGet(UCAL_DAY_OF_YEAR) + dow) % 7; 1973 // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here. 1974 1975 // Get the limit day for the blocked-off rectangular year; that 1976 // is, the day which is one past the last day of the year, 1977 // after the year has already been filled in with phantom days 1978 // to fill out the last week. This day has a normalized DOW of 0. 1979 int32_t limit = yearLen + 7 - ldy; 1980 1981 // Now roll between start and (limit - 1). 1982 int32_t gap = limit - start; 1983 int32_t day_of_year = (internalGet(UCAL_DAY_OF_YEAR) + amount*7 - 1984 start) % gap; 1985 if (day_of_year < 0) day_of_year += gap; 1986 day_of_year += start; 1987 1988 // Finally, pin to the real start and end of the month. 1989 if (day_of_year < 1) day_of_year = 1; 1990 if (day_of_year > yearLen) day_of_year = yearLen; 1991 1992 // Make sure that the year and day of year are attended to by 1993 // clearing other fields which would normally take precedence. 1994 // If the disambiguation algorithm is changed, this section will 1995 // have to be updated as well. 1996 set(UCAL_DAY_OF_YEAR, day_of_year); 1997 clear(UCAL_MONTH); 1998 return; 1999 } 2000 case UCAL_DAY_OF_YEAR: 2001 { 2002 // Roll the day of year using millis. Compute the millis for 2003 // the start of the year, and get the length of the year. 2004 double delta = amount * kOneDay; // Scale up from days to millis 2005 double min2 = internalGet(UCAL_DAY_OF_YEAR)-1; 2006 min2 *= kOneDay; 2007 min2 = internalGetTime() - min2; 2008 2009 // double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay; 2010 double newtime; 2011 2012 double yearLength = getActualMaximum(UCAL_DAY_OF_YEAR,status); 2013 double oneYear = yearLength; 2014 oneYear *= kOneDay; 2015 newtime = uprv_fmod((internalGetTime() + delta - min2), oneYear); 2016 if (newtime < 0) newtime += oneYear; 2017 setTimeInMillis(newtime + min2, status); 2018 return; 2019 } 2020 case UCAL_DAY_OF_WEEK: 2021 case UCAL_DOW_LOCAL: 2022 { 2023 // Roll the day of week using millis. Compute the millis for 2024 // the start of the week, using the first day of week setting. 2025 // Restrict the millis to [start, start+7days). 2026 double delta = amount * kOneDay; // Scale up from days to millis 2027 // Compute the number of days before the current day in this 2028 // week. This will be a value 0..6. 2029 int32_t leadDays = internalGet(field); 2030 leadDays -= (field == UCAL_DAY_OF_WEEK) ? getFirstDayOfWeek() : 1; 2031 if (leadDays < 0) leadDays += 7; 2032 double min2 = internalGetTime() - leadDays * kOneDay; 2033 double newtime = uprv_fmod((internalGetTime() + delta - min2), kOneWeek); 2034 if (newtime < 0) newtime += kOneWeek; 2035 setTimeInMillis(newtime + min2, status); 2036 return; 2037 } 2038 case UCAL_DAY_OF_WEEK_IN_MONTH: 2039 { 2040 // Roll the day of week in the month using millis. Determine 2041 // the first day of the week in the month, and then the last, 2042 // and then roll within that range. 2043 double delta = amount * kOneWeek; // Scale up from weeks to millis 2044 // Find the number of same days of the week before this one 2045 // in this month. 2046 int32_t preWeeks = (internalGet(UCAL_DAY_OF_MONTH) - 1) / 7; 2047 // Find the number of same days of the week after this one 2048 // in this month. 2049 int32_t postWeeks = (getActualMaximum(UCAL_DAY_OF_MONTH,status) - 2050 internalGet(UCAL_DAY_OF_MONTH)) / 7; 2051 // From these compute the min and gap millis for rolling. 2052 double min2 = internalGetTime() - preWeeks * kOneWeek; 2053 double gap2 = kOneWeek * (preWeeks + postWeeks + 1); // Must add 1! 2054 // Roll within this range 2055 double newtime = uprv_fmod((internalGetTime() + delta - min2), gap2); 2056 if (newtime < 0) newtime += gap2; 2057 setTimeInMillis(newtime + min2, status); 2058 return; 2059 } 2060 case UCAL_JULIAN_DAY: 2061 set(field, internalGet(field) + amount); 2062 return; 2063 default: 2064 // Other fields cannot be rolled by this method 2065 #if defined (U_DEBUG_CAL) 2066 fprintf(stderr, "%s:%d: ILLEGAL ARG because of roll on non-rollable field %s\n", 2067 __FILE__, __LINE__,fldName(field)); 2068 #endif 2069 status = U_ILLEGAL_ARGUMENT_ERROR; 2070 } 2071 } 2072 2073 void Calendar::add(EDateFields field, int32_t amount, UErrorCode& status) 2074 { 2075 Calendar::add((UCalendarDateFields)field, amount, status); 2076 } 2077 2078 // ------------------------------------- 2079 void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) 2080 { 2081 if (amount == 0) { 2082 return; // Do nothing! 2083 } 2084 2085 // We handle most fields in the same way. The algorithm is to add 2086 // a computed amount of millis to the current millis. The only 2087 // wrinkle is with DST (and/or a change to the zone's UTC offset, which 2088 // we'll include with DST) -- for some fields, like the DAY_OF_MONTH, 2089 // we don't want the wall time to shift due to changes in DST. If the 2090 // result of the add operation is to move from DST to Standard, or 2091 // vice versa, we need to adjust by an hour forward or back, 2092 // respectively. For such fields we set keepWallTimeInvariant to TRUE. 2093 2094 // We only adjust the DST for fields larger than an hour. For 2095 // fields smaller than an hour, we cannot adjust for DST without 2096 // causing problems. for instance, if you add one hour to April 5, 2097 // 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an 2098 // illegal value), but then the adjustment sees the change and 2099 // compensates by subtracting an hour. As a result the time 2100 // doesn't advance at all. 2101 2102 // For some fields larger than a day, such as a UCAL_MONTH, we pin the 2103 // UCAL_DAY_OF_MONTH. This allows <March 31>.add(UCAL_MONTH, 1) to be 2104 // <April 30>, rather than <April 31> => <May 1>. 2105 2106 double delta = amount; // delta in ms 2107 UBool keepWallTimeInvariant = TRUE; 2108 2109 switch (field) { 2110 case UCAL_ERA: 2111 set(field, get(field, status) + amount); 2112 pinField(UCAL_ERA, status); 2113 return; 2114 2115 case UCAL_YEAR: 2116 case UCAL_YEAR_WOY: 2117 { 2118 // * If era=0 and years go backwards in time, change sign of amount. 2119 // * Until we have new API per #9393, we temporarily hardcode knowledge of 2120 // which calendars have era 0 years that go backwards. 2121 // * Note that for UCAL_YEAR (but not UCAL_YEAR_WOY) we could instead handle 2122 // this by applying the amount to the UCAL_EXTENDED_YEAR field; but since 2123 // we would still need to handle UCAL_YEAR_WOY as below, might as well 2124 // also handle UCAL_YEAR the same way. 2125 int32_t era = get(UCAL_ERA, status); 2126 if (era == 0) { 2127 const char * calType = getType(); 2128 if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) { 2129 amount = -amount; 2130 } 2131 } 2132 } 2133 // Fall through into normal handling 2134 case UCAL_EXTENDED_YEAR: 2135 case UCAL_MONTH: 2136 { 2137 UBool oldLenient = isLenient(); 2138 setLenient(TRUE); 2139 set(field, get(field, status) + amount); 2140 pinField(UCAL_DAY_OF_MONTH, status); 2141 if(oldLenient==FALSE) { 2142 complete(status); /* force recalculate */ 2143 setLenient(oldLenient); 2144 } 2145 } 2146 return; 2147 2148 case UCAL_WEEK_OF_YEAR: 2149 case UCAL_WEEK_OF_MONTH: 2150 case UCAL_DAY_OF_WEEK_IN_MONTH: 2151 delta *= kOneWeek; 2152 break; 2153 2154 case UCAL_AM_PM: 2155 delta *= 12 * kOneHour; 2156 break; 2157 2158 case UCAL_DAY_OF_MONTH: 2159 case UCAL_DAY_OF_YEAR: 2160 case UCAL_DAY_OF_WEEK: 2161 case UCAL_DOW_LOCAL: 2162 case UCAL_JULIAN_DAY: 2163 delta *= kOneDay; 2164 break; 2165 2166 case UCAL_HOUR_OF_DAY: 2167 case UCAL_HOUR: 2168 delta *= kOneHour; 2169 keepWallTimeInvariant = FALSE; 2170 break; 2171 2172 case UCAL_MINUTE: 2173 delta *= kOneMinute; 2174 keepWallTimeInvariant = FALSE; 2175 break; 2176 2177 case UCAL_SECOND: 2178 delta *= kOneSecond; 2179 keepWallTimeInvariant = FALSE; 2180 break; 2181 2182 case UCAL_MILLISECOND: 2183 case UCAL_MILLISECONDS_IN_DAY: 2184 keepWallTimeInvariant = FALSE; 2185 break; 2186 2187 default: 2188 #if defined (U_DEBUG_CAL) 2189 fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s not addable", 2190 __FILE__, __LINE__, fldName(field)); 2191 #endif 2192 status = U_ILLEGAL_ARGUMENT_ERROR; 2193 return; 2194 // throw new IllegalArgumentException("Calendar.add(" + fieldName(field) + 2195 // ") not supported"); 2196 } 2197 2198 // In order to keep the wall time invariant (for fields where this is 2199 // appropriate), check the combined DST & ZONE offset before and 2200 // after the add() operation. If it changes, then adjust the millis 2201 // to compensate. 2202 int32_t prevOffset = 0; 2203 int32_t prevWallTime = 0; 2204 if (keepWallTimeInvariant) { 2205 prevOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status); 2206 prevWallTime = get(UCAL_MILLISECONDS_IN_DAY, status); 2207 } 2208 2209 setTimeInMillis(getTimeInMillis(status) + delta, status); 2210 2211 if (keepWallTimeInvariant) { 2212 int32_t newWallTime = get(UCAL_MILLISECONDS_IN_DAY, status); 2213 if (newWallTime != prevWallTime) { 2214 // There is at least one zone transition between the base 2215 // time and the result time. As the result, wall time has 2216 // changed. 2217 UDate t = internalGetTime(); 2218 int32_t newOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status); 2219 if (newOffset != prevOffset) { 2220 // When the difference of the previous UTC offset and 2221 // the new UTC offset exceeds 1 full day, we do not want 2222 // to roll over/back the date. For now, this only happens 2223 // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452. 2224 int32_t adjAmount = prevOffset - newOffset; 2225 adjAmount = adjAmount >= 0 ? adjAmount % (int32_t)kOneDay : -(-adjAmount % (int32_t)kOneDay); 2226 if (adjAmount != 0) { 2227 setTimeInMillis(t + adjAmount, status); 2228 newWallTime = get(UCAL_MILLISECONDS_IN_DAY, status); 2229 } 2230 if (newWallTime != prevWallTime) { 2231 // The result wall time or adjusted wall time was shifted because 2232 // the target wall time does not exist on the result date. 2233 switch (fSkippedWallTime) { 2234 case UCAL_WALLTIME_FIRST: 2235 if (adjAmount > 0) { 2236 setTimeInMillis(t, status); 2237 } 2238 break; 2239 case UCAL_WALLTIME_LAST: 2240 if (adjAmount < 0) { 2241 setTimeInMillis(t, status); 2242 } 2243 break; 2244 case UCAL_WALLTIME_NEXT_VALID: 2245 UDate tmpT = adjAmount > 0 ? internalGetTime() : t; 2246 UDate immediatePrevTrans; 2247 UBool hasTransition = getImmediatePreviousZoneTransition(tmpT, &immediatePrevTrans, status); 2248 if (U_SUCCESS(status) && hasTransition) { 2249 setTimeInMillis(immediatePrevTrans, status); 2250 } 2251 break; 2252 } 2253 } 2254 } 2255 } 2256 } 2257 } 2258 2259 // ------------------------------------- 2260 int32_t Calendar::fieldDifference(UDate when, EDateFields field, UErrorCode& status) { 2261 return fieldDifference(when, (UCalendarDateFields) field, status); 2262 } 2263 2264 int32_t Calendar::fieldDifference(UDate targetMs, UCalendarDateFields field, UErrorCode& ec) { 2265 if (U_FAILURE(ec)) return 0; 2266 int32_t min = 0; 2267 double startMs = getTimeInMillis(ec); 2268 // Always add from the start millis. This accomodates 2269 // operations like adding years from February 29, 2000 up to 2270 // February 29, 2004. If 1, 1, 1, 1 is added to the year 2271 // field, the DOM gets pinned to 28 and stays there, giving an 2272 // incorrect DOM difference of 1. We have to add 1, reset, 2, 2273 // reset, 3, reset, 4. 2274 if (startMs < targetMs) { 2275 int32_t max = 1; 2276 // Find a value that is too large 2277 while (U_SUCCESS(ec)) { 2278 setTimeInMillis(startMs, ec); 2279 add(field, max, ec); 2280 double ms = getTimeInMillis(ec); 2281 if (ms == targetMs) { 2282 return max; 2283 } else if (ms > targetMs) { 2284 break; 2285 } else if (max < INT32_MAX) { 2286 min = max; 2287 max <<= 1; 2288 if (max < 0) { 2289 max = INT32_MAX; 2290 } 2291 } else { 2292 // Field difference too large to fit into int32_t 2293 #if defined (U_DEBUG_CAL) 2294 fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n", 2295 __FILE__, __LINE__, fldName(field)); 2296 #endif 2297 ec = U_ILLEGAL_ARGUMENT_ERROR; 2298 } 2299 } 2300 // Do a binary search 2301 while ((max - min) > 1 && U_SUCCESS(ec)) { 2302 int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX 2303 setTimeInMillis(startMs, ec); 2304 add(field, t, ec); 2305 double ms = getTimeInMillis(ec); 2306 if (ms == targetMs) { 2307 return t; 2308 } else if (ms > targetMs) { 2309 max = t; 2310 } else { 2311 min = t; 2312 } 2313 } 2314 } else if (startMs > targetMs) { 2315 int32_t max = -1; 2316 // Find a value that is too small 2317 while (U_SUCCESS(ec)) { 2318 setTimeInMillis(startMs, ec); 2319 add(field, max, ec); 2320 double ms = getTimeInMillis(ec); 2321 if (ms == targetMs) { 2322 return max; 2323 } else if (ms < targetMs) { 2324 break; 2325 } else { 2326 min = max; 2327 max <<= 1; 2328 if (max == 0) { 2329 // Field difference too large to fit into int32_t 2330 #if defined (U_DEBUG_CAL) 2331 fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n", 2332 __FILE__, __LINE__, fldName(field)); 2333 #endif 2334 ec = U_ILLEGAL_ARGUMENT_ERROR; 2335 } 2336 } 2337 } 2338 // Do a binary search 2339 while ((min - max) > 1 && U_SUCCESS(ec)) { 2340 int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX 2341 setTimeInMillis(startMs, ec); 2342 add(field, t, ec); 2343 double ms = getTimeInMillis(ec); 2344 if (ms == targetMs) { 2345 return t; 2346 } else if (ms < targetMs) { 2347 max = t; 2348 } else { 2349 min = t; 2350 } 2351 } 2352 } 2353 // Set calendar to end point 2354 setTimeInMillis(startMs, ec); 2355 add(field, min, ec); 2356 2357 /* Test for buffer overflows */ 2358 if(U_FAILURE(ec)) { 2359 return 0; 2360 } 2361 return min; 2362 } 2363 2364 // ------------------------------------- 2365 2366 void 2367 Calendar::adoptTimeZone(TimeZone* zone) 2368 { 2369 // Do nothing if passed-in zone is NULL 2370 if (zone == NULL) return; 2371 2372 // fZone should always be non-null 2373 delete fZone; 2374 fZone = zone; 2375 2376 // if the zone changes, we need to recompute the time fields 2377 fAreFieldsSet = FALSE; 2378 } 2379 2380 // ------------------------------------- 2381 void 2382 Calendar::setTimeZone(const TimeZone& zone) 2383 { 2384 adoptTimeZone(zone.clone()); 2385 } 2386 2387 // ------------------------------------- 2388 2389 const TimeZone& 2390 Calendar::getTimeZone() const 2391 { 2392 U_ASSERT(fZone != NULL); 2393 return *fZone; 2394 } 2395 2396 // ------------------------------------- 2397 2398 TimeZone* 2399 Calendar::orphanTimeZone() 2400 { 2401 // we let go of the time zone; the new time zone is the system default time zone 2402 TimeZone *defaultZone = TimeZone::createDefault(); 2403 if (defaultZone == NULL) { 2404 // No error handling available. Must keep fZone non-NULL, there are many unchecked uses. 2405 return NULL; 2406 } 2407 TimeZone *z = fZone; 2408 fZone = defaultZone; 2409 return z; 2410 } 2411 2412 // ------------------------------------- 2413 2414 void 2415 Calendar::setLenient(UBool lenient) 2416 { 2417 fLenient = lenient; 2418 } 2419 2420 // ------------------------------------- 2421 2422 UBool 2423 Calendar::isLenient() const 2424 { 2425 return fLenient; 2426 } 2427 2428 // ------------------------------------- 2429 2430 void 2431 Calendar::setRepeatedWallTimeOption(UCalendarWallTimeOption option) 2432 { 2433 if (option == UCAL_WALLTIME_LAST || option == UCAL_WALLTIME_FIRST) { 2434 fRepeatedWallTime = option; 2435 } 2436 } 2437 2438 // ------------------------------------- 2439 2440 UCalendarWallTimeOption 2441 Calendar::getRepeatedWallTimeOption(void) const 2442 { 2443 return fRepeatedWallTime; 2444 } 2445 2446 // ------------------------------------- 2447 2448 void 2449 Calendar::setSkippedWallTimeOption(UCalendarWallTimeOption option) 2450 { 2451 fSkippedWallTime = option; 2452 } 2453 2454 // ------------------------------------- 2455 2456 UCalendarWallTimeOption 2457 Calendar::getSkippedWallTimeOption(void) const 2458 { 2459 return fSkippedWallTime; 2460 } 2461 2462 // ------------------------------------- 2463 2464 void 2465 Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value) 2466 { 2467 if (fFirstDayOfWeek != value && 2468 value >= UCAL_SUNDAY && value <= UCAL_SATURDAY) { 2469 fFirstDayOfWeek = value; 2470 fAreFieldsSet = FALSE; 2471 } 2472 } 2473 2474 // ------------------------------------- 2475 2476 Calendar::EDaysOfWeek 2477 Calendar::getFirstDayOfWeek() const 2478 { 2479 return (Calendar::EDaysOfWeek)fFirstDayOfWeek; 2480 } 2481 2482 UCalendarDaysOfWeek 2483 Calendar::getFirstDayOfWeek(UErrorCode & /*status*/) const 2484 { 2485 return fFirstDayOfWeek; 2486 } 2487 // ------------------------------------- 2488 2489 void 2490 Calendar::setMinimalDaysInFirstWeek(uint8_t value) 2491 { 2492 // Values less than 1 have the same effect as 1; values greater 2493 // than 7 have the same effect as 7. However, we normalize values 2494 // so operator== and so forth work. 2495 if (value < 1) { 2496 value = 1; 2497 } else if (value > 7) { 2498 value = 7; 2499 } 2500 if (fMinimalDaysInFirstWeek != value) { 2501 fMinimalDaysInFirstWeek = value; 2502 fAreFieldsSet = FALSE; 2503 } 2504 } 2505 2506 // ------------------------------------- 2507 2508 uint8_t 2509 Calendar::getMinimalDaysInFirstWeek() const 2510 { 2511 return fMinimalDaysInFirstWeek; 2512 } 2513 2514 // ------------------------------------- 2515 // weekend functions, just dummy implementations for now (for API freeze) 2516 2517 UCalendarWeekdayType 2518 Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const 2519 { 2520 if (U_FAILURE(status)) { 2521 return UCAL_WEEKDAY; 2522 } 2523 if (dayOfWeek < UCAL_SUNDAY || dayOfWeek > UCAL_SATURDAY) { 2524 status = U_ILLEGAL_ARGUMENT_ERROR; 2525 return UCAL_WEEKDAY; 2526 } 2527 if (fWeekendOnset == fWeekendCease) { 2528 if (dayOfWeek != fWeekendOnset) 2529 return UCAL_WEEKDAY; 2530 return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET; 2531 } 2532 if (fWeekendOnset < fWeekendCease) { 2533 if (dayOfWeek < fWeekendOnset || dayOfWeek > fWeekendCease) { 2534 return UCAL_WEEKDAY; 2535 } 2536 } else { 2537 if (dayOfWeek > fWeekendCease && dayOfWeek < fWeekendOnset) { 2538 return UCAL_WEEKDAY; 2539 } 2540 } 2541 if (dayOfWeek == fWeekendOnset) { 2542 return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET; 2543 } 2544 if (dayOfWeek == fWeekendCease) { 2545 return (fWeekendCeaseMillis >= 86400000) ? UCAL_WEEKEND : UCAL_WEEKEND_CEASE; 2546 } 2547 return UCAL_WEEKEND; 2548 } 2549 2550 int32_t 2551 Calendar::getWeekendTransition(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const 2552 { 2553 if (U_FAILURE(status)) { 2554 return 0; 2555 } 2556 if (dayOfWeek == fWeekendOnset) { 2557 return fWeekendOnsetMillis; 2558 } else if (dayOfWeek == fWeekendCease) { 2559 return fWeekendCeaseMillis; 2560 } 2561 status = U_ILLEGAL_ARGUMENT_ERROR; 2562 return 0; 2563 } 2564 2565 UBool 2566 Calendar::isWeekend(UDate date, UErrorCode &status) const 2567 { 2568 if (U_FAILURE(status)) { 2569 return FALSE; 2570 } 2571 // clone the calendar so we don't mess with the real one. 2572 Calendar *work = (Calendar*)this->clone(); 2573 if (work == NULL) { 2574 status = U_MEMORY_ALLOCATION_ERROR; 2575 return FALSE; 2576 } 2577 UBool result = FALSE; 2578 work->setTime(date, status); 2579 if (U_SUCCESS(status)) { 2580 result = work->isWeekend(); 2581 } 2582 delete work; 2583 return result; 2584 } 2585 2586 UBool 2587 Calendar::isWeekend(void) const 2588 { 2589 UErrorCode status = U_ZERO_ERROR; 2590 UCalendarDaysOfWeek dayOfWeek = (UCalendarDaysOfWeek)get(UCAL_DAY_OF_WEEK, status); 2591 UCalendarWeekdayType dayType = getDayOfWeekType(dayOfWeek, status); 2592 if (U_SUCCESS(status)) { 2593 switch (dayType) { 2594 case UCAL_WEEKDAY: 2595 return FALSE; 2596 case UCAL_WEEKEND: 2597 return TRUE; 2598 case UCAL_WEEKEND_ONSET: 2599 case UCAL_WEEKEND_CEASE: 2600 // Use internalGet() because the above call to get() populated all fields. 2601 { 2602 int32_t millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY); 2603 int32_t transitionMillis = getWeekendTransition(dayOfWeek, status); 2604 if (U_SUCCESS(status)) { 2605 return (dayType == UCAL_WEEKEND_ONSET)? 2606 (millisInDay >= transitionMillis): 2607 (millisInDay < transitionMillis); 2608 } 2609 // else fall through, return FALSE 2610 } 2611 default: 2612 break; 2613 } 2614 } 2615 return FALSE; 2616 } 2617 2618 // ------------------------------------- limits 2619 2620 int32_t 2621 Calendar::getMinimum(EDateFields field) const { 2622 return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MINIMUM); 2623 } 2624 2625 int32_t 2626 Calendar::getMinimum(UCalendarDateFields field) const 2627 { 2628 return getLimit(field,UCAL_LIMIT_MINIMUM); 2629 } 2630 2631 // ------------------------------------- 2632 int32_t 2633 Calendar::getMaximum(EDateFields field) const 2634 { 2635 return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MAXIMUM); 2636 } 2637 2638 int32_t 2639 Calendar::getMaximum(UCalendarDateFields field) const 2640 { 2641 return getLimit(field,UCAL_LIMIT_MAXIMUM); 2642 } 2643 2644 // ------------------------------------- 2645 int32_t 2646 Calendar::getGreatestMinimum(EDateFields field) const 2647 { 2648 return getLimit((UCalendarDateFields)field,UCAL_LIMIT_GREATEST_MINIMUM); 2649 } 2650 2651 int32_t 2652 Calendar::getGreatestMinimum(UCalendarDateFields field) const 2653 { 2654 return getLimit(field,UCAL_LIMIT_GREATEST_MINIMUM); 2655 } 2656 2657 // ------------------------------------- 2658 int32_t 2659 Calendar::getLeastMaximum(EDateFields field) const 2660 { 2661 return getLimit((UCalendarDateFields) field,UCAL_LIMIT_LEAST_MAXIMUM); 2662 } 2663 2664 int32_t 2665 Calendar::getLeastMaximum(UCalendarDateFields field) const 2666 { 2667 return getLimit( field,UCAL_LIMIT_LEAST_MAXIMUM); 2668 } 2669 2670 // ------------------------------------- 2671 int32_t 2672 Calendar::getActualMinimum(EDateFields field, UErrorCode& status) const 2673 { 2674 return getActualMinimum((UCalendarDateFields) field, status); 2675 } 2676 2677 int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) const { 2678 switch (field) { 2679 case UCAL_DAY_OF_WEEK: 2680 case UCAL_AM_PM: 2681 case UCAL_HOUR: 2682 case UCAL_HOUR_OF_DAY: 2683 case UCAL_MINUTE: 2684 case UCAL_SECOND: 2685 case UCAL_MILLISECOND: 2686 case UCAL_ZONE_OFFSET: 2687 case UCAL_DST_OFFSET: 2688 case UCAL_DOW_LOCAL: 2689 case UCAL_JULIAN_DAY: 2690 case UCAL_MILLISECONDS_IN_DAY: 2691 case UCAL_IS_LEAP_MONTH: 2692 return kCalendarLimits[field][limitType]; 2693 2694 case UCAL_WEEK_OF_MONTH: 2695 { 2696 int32_t limit; 2697 if (limitType == UCAL_LIMIT_MINIMUM) { 2698 limit = getMinimalDaysInFirstWeek() == 1 ? 1 : 0; 2699 } else if (limitType == UCAL_LIMIT_GREATEST_MINIMUM) { 2700 limit = 1; 2701 } else { 2702 int32_t minDaysInFirst = getMinimalDaysInFirstWeek(); 2703 int32_t daysInMonth = handleGetLimit(UCAL_DAY_OF_MONTH, limitType); 2704 if (limitType == UCAL_LIMIT_LEAST_MAXIMUM) { 2705 limit = (daysInMonth + (7 - minDaysInFirst)) / 7; 2706 } else { // limitType == UCAL_LIMIT_MAXIMUM 2707 limit = (daysInMonth + 6 + (7 - minDaysInFirst)) / 7; 2708 } 2709 } 2710 return limit; 2711 } 2712 default: 2713 return handleGetLimit(field, limitType); 2714 } 2715 } 2716 2717 2718 int32_t 2719 Calendar::getActualMinimum(UCalendarDateFields field, UErrorCode& status) const 2720 { 2721 int32_t fieldValue = getGreatestMinimum(field); 2722 int32_t endValue = getMinimum(field); 2723 2724 // if we know that the minimum value is always the same, just return it 2725 if (fieldValue == endValue) { 2726 return fieldValue; 2727 } 2728 2729 // clone the calendar so we don't mess with the real one, and set it to 2730 // accept anything for the field values 2731 Calendar *work = (Calendar*)this->clone(); 2732 if (work == NULL) { 2733 status = U_MEMORY_ALLOCATION_ERROR; 2734 return 0; 2735 } 2736 work->setLenient(TRUE); 2737 2738 // now try each value from getLeastMaximum() to getMaximum() one by one until 2739 // we get a value that normalizes to another value. The last value that 2740 // normalizes to itself is the actual minimum for the current date 2741 int32_t result = fieldValue; 2742 2743 do { 2744 work->set(field, fieldValue); 2745 if (work->get(field, status) != fieldValue) { 2746 break; 2747 } 2748 else { 2749 result = fieldValue; 2750 fieldValue--; 2751 } 2752 } while (fieldValue >= endValue); 2753 2754 delete work; 2755 2756 /* Test for buffer overflows */ 2757 if(U_FAILURE(status)) { 2758 return 0; 2759 } 2760 return result; 2761 } 2762 2763 // ------------------------------------- 2764 2765 2766 2767 /** 2768 * Ensure that each field is within its valid range by calling {@link 2769 * #validateField(int)} on each field that has been set. This method 2770 * should only be called if this calendar is not lenient. 2771 * @see #isLenient 2772 * @see #validateField(int) 2773 */ 2774 void Calendar::validateFields(UErrorCode &status) { 2775 for (int32_t field = 0; U_SUCCESS(status) && (field < UCAL_FIELD_COUNT); field++) { 2776 if (fStamp[field] >= kMinimumUserStamp) { 2777 validateField((UCalendarDateFields)field, status); 2778 } 2779 } 2780 } 2781 2782 /** 2783 * Validate a single field of this calendar. Subclasses should 2784 * override this method to validate any calendar-specific fields. 2785 * Generic fields can be handled by 2786 * <code>Calendar.validateField()</code>. 2787 * @see #validateField(int, int, int) 2788 */ 2789 void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) { 2790 int32_t y; 2791 switch (field) { 2792 case UCAL_DAY_OF_MONTH: 2793 y = handleGetExtendedYear(); 2794 validateField(field, 1, handleGetMonthLength(y, internalGet(UCAL_MONTH)), status); 2795 break; 2796 case UCAL_DAY_OF_YEAR: 2797 y = handleGetExtendedYear(); 2798 validateField(field, 1, handleGetYearLength(y), status); 2799 break; 2800 case UCAL_DAY_OF_WEEK_IN_MONTH: 2801 if (internalGet(field) == 0) { 2802 #if defined (U_DEBUG_CAL) 2803 fprintf(stderr, "%s:%d: ILLEGAL ARG because DOW in month cannot be 0\n", 2804 __FILE__, __LINE__); 2805 #endif 2806 status = U_ILLEGAL_ARGUMENT_ERROR; // "DAY_OF_WEEK_IN_MONTH cannot be zero" 2807 return; 2808 } 2809 validateField(field, getMinimum(field), getMaximum(field), status); 2810 break; 2811 default: 2812 validateField(field, getMinimum(field), getMaximum(field), status); 2813 break; 2814 } 2815 } 2816 2817 /** 2818 * Validate a single field of this calendar given its minimum and 2819 * maximum allowed value. If the field is out of range, throw a 2820 * descriptive <code>IllegalArgumentException</code>. Subclasses may 2821 * use this method in their implementation of {@link 2822 * #validateField(int)}. 2823 */ 2824 void Calendar::validateField(UCalendarDateFields field, int32_t min, int32_t max, UErrorCode& status) 2825 { 2826 int32_t value = fFields[field]; 2827 if (value < min || value > max) { 2828 #if defined (U_DEBUG_CAL) 2829 fprintf(stderr, "%s:%d: ILLEGAL ARG because of field %s out of range %d..%d at %d\n", 2830 __FILE__, __LINE__,fldName(field),min,max,value); 2831 #endif 2832 status = U_ILLEGAL_ARGUMENT_ERROR; 2833 return; 2834 } 2835 } 2836 2837 // ------------------------- 2838 2839 const UFieldResolutionTable* Calendar::getFieldResolutionTable() const { 2840 return kDatePrecedence; 2841 } 2842 2843 2844 UCalendarDateFields Calendar::newerField(UCalendarDateFields defaultField, UCalendarDateFields alternateField) const 2845 { 2846 if (fStamp[alternateField] > fStamp[defaultField]) { 2847 return alternateField; 2848 } 2849 return defaultField; 2850 } 2851 2852 UCalendarDateFields Calendar::resolveFields(const UFieldResolutionTable* precedenceTable) { 2853 int32_t bestField = UCAL_FIELD_COUNT; 2854 int32_t tempBestField; 2855 for (int32_t g=0; precedenceTable[g][0][0] != -1 && (bestField == UCAL_FIELD_COUNT); ++g) { 2856 int32_t bestStamp = kUnset; 2857 for (int32_t l=0; precedenceTable[g][l][0] != -1; ++l) { 2858 int32_t lineStamp = kUnset; 2859 // Skip over first entry if it is negative 2860 for (int32_t i=((precedenceTable[g][l][0]>=kResolveRemap)?1:0); precedenceTable[g][l][i]!=-1; ++i) { 2861 U_ASSERT(precedenceTable[g][l][i] < UCAL_FIELD_COUNT); 2862 int32_t s = fStamp[precedenceTable[g][l][i]]; 2863 // If any field is unset then don't use this line 2864 if (s == kUnset) { 2865 goto linesInGroup; 2866 } else if(s > lineStamp) { 2867 lineStamp = s; 2868 } 2869 } 2870 // Record new maximum stamp & field no. 2871 if (lineStamp > bestStamp) { 2872 tempBestField = precedenceTable[g][l][0]; // First field refers to entire line 2873 if (tempBestField >= kResolveRemap) { 2874 tempBestField &= (kResolveRemap-1); 2875 // This check is needed to resolve some issues with UCAL_YEAR precedence mapping 2876 if (tempBestField != UCAL_DATE || (fStamp[UCAL_WEEK_OF_MONTH] < fStamp[tempBestField])) { 2877 bestField = tempBestField; 2878 } 2879 } else { 2880 bestField = tempBestField; 2881 } 2882 2883 if (bestField == tempBestField) { 2884 bestStamp = lineStamp; 2885 } 2886 } 2887 linesInGroup: 2888 ; 2889 } 2890 } 2891 return (UCalendarDateFields)bestField; 2892 } 2893 2894 const UFieldResolutionTable Calendar::kDatePrecedence[] = 2895 { 2896 { 2897 { UCAL_DAY_OF_MONTH, kResolveSTOP }, 2898 { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP }, 2899 { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 2900 { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 2901 { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP }, 2902 { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 2903 { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 2904 { UCAL_DAY_OF_YEAR, kResolveSTOP }, 2905 { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_YEAR, kResolveSTOP }, // if YEAR is set over YEAR_WOY use DAY_OF_MONTH 2906 { kResolveRemap | UCAL_WEEK_OF_YEAR, UCAL_YEAR_WOY, kResolveSTOP }, // if YEAR_WOY is set, calc based on WEEK_OF_YEAR 2907 { kResolveSTOP } 2908 }, 2909 { 2910 { UCAL_WEEK_OF_YEAR, kResolveSTOP }, 2911 { UCAL_WEEK_OF_MONTH, kResolveSTOP }, 2912 { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP }, 2913 { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 2914 { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 2915 { kResolveSTOP } 2916 }, 2917 {{kResolveSTOP}} 2918 }; 2919 2920 2921 const UFieldResolutionTable Calendar::kDOWPrecedence[] = 2922 { 2923 { 2924 { UCAL_DAY_OF_WEEK,kResolveSTOP, kResolveSTOP }, 2925 { UCAL_DOW_LOCAL,kResolveSTOP, kResolveSTOP }, 2926 {kResolveSTOP} 2927 }, 2928 {{kResolveSTOP}} 2929 }; 2930 2931 // precedence for calculating a year 2932 const UFieldResolutionTable Calendar::kYearPrecedence[] = 2933 { 2934 { 2935 { UCAL_YEAR, kResolveSTOP }, 2936 { UCAL_EXTENDED_YEAR, kResolveSTOP }, 2937 { UCAL_YEAR_WOY, UCAL_WEEK_OF_YEAR, kResolveSTOP }, // YEAR_WOY is useless without WEEK_OF_YEAR 2938 { kResolveSTOP } 2939 }, 2940 {{kResolveSTOP}} 2941 }; 2942 2943 2944 // ------------------------- 2945 2946 2947 void Calendar::computeTime(UErrorCode& status) { 2948 if (!isLenient()) { 2949 validateFields(status); 2950 if (U_FAILURE(status)) { 2951 return; 2952 } 2953 } 2954 2955 // Compute the Julian day 2956 int32_t julianDay = computeJulianDay(); 2957 2958 double millis = Grego::julianDayToMillis(julianDay); 2959 2960 #if defined (U_DEBUG_CAL) 2961 // int32_t julianInsanityCheck = (int32_t)ClockMath::floorDivide(millis, kOneDay); 2962 // julianInsanityCheck += kEpochStartAsJulianDay; 2963 // if(1 || julianInsanityCheck != julianDay) { 2964 // fprintf(stderr, "%s:%d- D'oh- computed jules %d, to mills (%s)%.lf, recomputed %d\n", 2965 // __FILE__, __LINE__, julianDay, millis<0.0?"NEG":"", millis, julianInsanityCheck); 2966 // } 2967 #endif 2968 2969 int32_t millisInDay; 2970 2971 // We only use MILLISECONDS_IN_DAY if it has been set by the user. 2972 // This makes it possible for the caller to set the calendar to a 2973 // time and call clear(MONTH) to reset the MONTH to January. This 2974 // is legacy behavior. Without this, clear(MONTH) has no effect, 2975 // since the internally set JULIAN_DAY is used. 2976 if (fStamp[UCAL_MILLISECONDS_IN_DAY] >= ((int32_t)kMinimumUserStamp) && 2977 newestStamp(UCAL_AM_PM, UCAL_MILLISECOND, kUnset) <= fStamp[UCAL_MILLISECONDS_IN_DAY]) { 2978 millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY); 2979 } else { 2980 millisInDay = computeMillisInDay(); 2981 } 2982 2983 UDate t = 0; 2984 if (fStamp[UCAL_ZONE_OFFSET] >= ((int32_t)kMinimumUserStamp) || fStamp[UCAL_DST_OFFSET] >= ((int32_t)kMinimumUserStamp)) { 2985 t = millis + millisInDay - (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET)); 2986 } else { 2987 // Compute the time zone offset and DST offset. There are two potential 2988 // ambiguities here. We'll assume a 2:00 am (wall time) switchover time 2989 // for discussion purposes here. 2990 // 2991 // 1. The positive offset change such as transition into DST. 2992 // Here, a designated time of 2:00 am - 2:59 am does not actually exist. 2993 // For this case, skippedWallTime option specifies the behavior. 2994 // For example, 2:30 am is interpreted as; 2995 // - WALLTIME_LAST(default): 3:30 am (DST) (interpreting 2:30 am as 31 minutes after 1:59 am (STD)) 2996 // - WALLTIME_FIRST: 1:30 am (STD) (interpreting 2:30 am as 30 minutes before 3:00 am (DST)) 2997 // - WALLTIME_NEXT_VALID: 3:00 am (DST) (next valid time after 2:30 am on a wall clock) 2998 // 2. The negative offset change such as transition out of DST. 2999 // Here, a designated time of 1:00 am - 1:59 am can be in standard or DST. Both are valid 3000 // representations (the rep jumps from 1:59:59 DST to 1:00:00 Std). 3001 // For this case, repeatedWallTime option specifies the behavior. 3002 // For example, 1:30 am is interpreted as; 3003 // - WALLTIME_LAST(default): 1:30 am (STD) - latter occurrence 3004 // - WALLTIME_FIRST: 1:30 am (DST) - former occurrence 3005 // 3006 // In addition to above, when calendar is strict (not default), wall time falls into 3007 // the skipped time range will be processed as an error case. 3008 // 3009 // These special cases are mostly handled in #computeZoneOffset(long), except WALLTIME_NEXT_VALID 3010 // at positive offset change. The protected method computeZoneOffset(long) is exposed to Calendar 3011 // subclass implementations and marked as @stable. Strictly speaking, WALLTIME_NEXT_VALID 3012 // should be also handled in the same place, but we cannot change the code flow without deprecating 3013 // the protected method. 3014 // 3015 // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET 3016 // or DST_OFFSET fields; then we use those fields. 3017 3018 if (!isLenient() || fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID) { 3019 // When strict, invalidate a wall time falls into a skipped wall time range. 3020 // When lenient and skipped wall time option is WALLTIME_NEXT_VALID, 3021 // the result time will be adjusted to the next valid time (on wall clock). 3022 int32_t zoneOffset = computeZoneOffset(millis, millisInDay, status); 3023 UDate tmpTime = millis + millisInDay - zoneOffset; 3024 3025 int32_t raw, dst; 3026 fZone->getOffset(tmpTime, FALSE, raw, dst, status); 3027 3028 if (U_SUCCESS(status)) { 3029 // zoneOffset != (raw + dst) only when the given wall time fall into 3030 // a skipped wall time range caused by positive zone offset transition. 3031 if (zoneOffset != (raw + dst)) { 3032 if (!isLenient()) { 3033 status = U_ILLEGAL_ARGUMENT_ERROR; 3034 } else { 3035 U_ASSERT(fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID); 3036 // Adjust time to the next valid wall clock time. 3037 // At this point, tmpTime is on or after the zone offset transition causing 3038 // the skipped time range. 3039 UDate immediatePrevTransition; 3040 UBool hasTransition = getImmediatePreviousZoneTransition(tmpTime, &immediatePrevTransition, status); 3041 if (U_SUCCESS(status) && hasTransition) { 3042 t = immediatePrevTransition; 3043 } 3044 } 3045 } else { 3046 t = tmpTime; 3047 } 3048 } 3049 } else { 3050 t = millis + millisInDay - computeZoneOffset(millis, millisInDay, status); 3051 } 3052 } 3053 if (U_SUCCESS(status)) { 3054 internalSetTime(t); 3055 } 3056 } 3057 3058 /** 3059 * Find the previous zone transtion near the given time. 3060 */ 3061 UBool Calendar::getImmediatePreviousZoneTransition(UDate base, UDate *transitionTime, UErrorCode& status) const { 3062 BasicTimeZone *btz = getBasicTimeZone(); 3063 if (btz) { 3064 TimeZoneTransition trans; 3065 UBool hasTransition = btz->getPreviousTransition(base, TRUE, trans); 3066 if (hasTransition) { 3067 *transitionTime = trans.getTime(); 3068 return TRUE; 3069 } else { 3070 // Could not find any transitions. 3071 // Note: This should never happen. 3072 status = U_INTERNAL_PROGRAM_ERROR; 3073 } 3074 } else { 3075 // If not BasicTimeZone, return unsupported error for now. 3076 // TODO: We may support non-BasicTimeZone in future. 3077 status = U_UNSUPPORTED_ERROR; 3078 } 3079 return FALSE; 3080 } 3081 3082 /** 3083 * Compute the milliseconds in the day from the fields. This is a 3084 * value from 0 to 23:59:59.999 inclusive, unless fields are out of 3085 * range, in which case it can be an arbitrary value. This value 3086 * reflects local zone wall time. 3087 * @stable ICU 2.0 3088 */ 3089 int32_t Calendar::computeMillisInDay() { 3090 // Do the time portion of the conversion. 3091 3092 int32_t millisInDay = 0; 3093 3094 // Find the best set of fields specifying the time of day. There 3095 // are only two possibilities here; the HOUR_OF_DAY or the 3096 // AM_PM and the HOUR. 3097 int32_t hourOfDayStamp = fStamp[UCAL_HOUR_OF_DAY]; 3098 int32_t hourStamp = (fStamp[UCAL_HOUR] > fStamp[UCAL_AM_PM])?fStamp[UCAL_HOUR]:fStamp[UCAL_AM_PM]; 3099 int32_t bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp; 3100 3101 // Hours 3102 if (bestStamp != kUnset) { 3103 if (bestStamp == hourOfDayStamp) { 3104 // Don't normalize here; let overflow bump into the next period. 3105 // This is consistent with how we handle other fields. 3106 millisInDay += internalGet(UCAL_HOUR_OF_DAY); 3107 } else { 3108 // Don't normalize here; let overflow bump into the next period. 3109 // This is consistent with how we handle other fields. 3110 millisInDay += internalGet(UCAL_HOUR); 3111 millisInDay += 12 * internalGet(UCAL_AM_PM); // Default works for unset AM_PM 3112 } 3113 } 3114 3115 // We use the fact that unset == 0; we start with millisInDay 3116 // == HOUR_OF_DAY. 3117 millisInDay *= 60; 3118 millisInDay += internalGet(UCAL_MINUTE); // now have minutes 3119 millisInDay *= 60; 3120 millisInDay += internalGet(UCAL_SECOND); // now have seconds 3121 millisInDay *= 1000; 3122 millisInDay += internalGet(UCAL_MILLISECOND); // now have millis 3123 3124 return millisInDay; 3125 } 3126 3127 /** 3128 * This method can assume EXTENDED_YEAR has been set. 3129 * @param millis milliseconds of the date fields 3130 * @param millisInDay milliseconds of the time fields; may be out 3131 * or range. 3132 * @stable ICU 2.0 3133 */ 3134 int32_t Calendar::computeZoneOffset(double millis, int32_t millisInDay, UErrorCode &ec) { 3135 int32_t rawOffset, dstOffset; 3136 UDate wall = millis + millisInDay; 3137 BasicTimeZone* btz = getBasicTimeZone(); 3138 if (btz) { 3139 int duplicatedTimeOpt = (fRepeatedWallTime == UCAL_WALLTIME_FIRST) ? BasicTimeZone::kFormer : BasicTimeZone::kLatter; 3140 int nonExistingTimeOpt = (fSkippedWallTime == UCAL_WALLTIME_FIRST) ? BasicTimeZone::kLatter : BasicTimeZone::kFormer; 3141 btz->getOffsetFromLocal(wall, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, ec); 3142 } else { 3143 const TimeZone& tz = getTimeZone(); 3144 // By default, TimeZone::getOffset behaves UCAL_WALLTIME_LAST for both. 3145 tz.getOffset(wall, TRUE, rawOffset, dstOffset, ec); 3146 3147 UBool sawRecentNegativeShift = FALSE; 3148 if (fRepeatedWallTime == UCAL_WALLTIME_FIRST) { 3149 // Check if the given wall time falls into repeated time range 3150 UDate tgmt = wall - (rawOffset + dstOffset); 3151 3152 // Any negative zone transition within last 6 hours? 3153 // Note: The maximum historic negative zone transition is -3 hours in the tz database. 3154 // 6 hour window would be sufficient for this purpose. 3155 int32_t tmpRaw, tmpDst; 3156 tz.getOffset(tgmt - 6*60*60*1000, FALSE, tmpRaw, tmpDst, ec); 3157 int32_t offsetDelta = (rawOffset + dstOffset) - (tmpRaw + tmpDst); 3158 3159 U_ASSERT(offsetDelta < -6*60*60*1000); 3160 if (offsetDelta < 0) { 3161 sawRecentNegativeShift = TRUE; 3162 // Negative shift within last 6 hours. When UCAL_WALLTIME_FIRST is used and the given wall time falls 3163 // into the repeated time range, use offsets before the transition. 3164 // Note: If it does not fall into the repeated time range, offsets remain unchanged below. 3165 tz.getOffset(wall + offsetDelta, TRUE, rawOffset, dstOffset, ec); 3166 } 3167 } 3168 if (!sawRecentNegativeShift && fSkippedWallTime == UCAL_WALLTIME_FIRST) { 3169 // When skipped wall time option is WALLTIME_FIRST, 3170 // recalculate offsets from the resolved time (non-wall). 3171 // When the given wall time falls into skipped wall time, 3172 // the offsets will be based on the zone offsets AFTER 3173 // the transition (which means, earliest possibe interpretation). 3174 UDate tgmt = wall - (rawOffset + dstOffset); 3175 tz.getOffset(tgmt, FALSE, rawOffset, dstOffset, ec); 3176 } 3177 } 3178 return rawOffset + dstOffset; 3179 } 3180 3181 int32_t Calendar::computeJulianDay() 3182 { 3183 // We want to see if any of the date fields is newer than the 3184 // JULIAN_DAY. If not, then we use JULIAN_DAY. If so, then we do 3185 // the normal resolution. We only use JULIAN_DAY if it has been 3186 // set by the user. This makes it possible for the caller to set 3187 // the calendar to a time and call clear(MONTH) to reset the MONTH 3188 // to January. This is legacy behavior. Without this, 3189 // clear(MONTH) has no effect, since the internally set JULIAN_DAY 3190 // is used. 3191 if (fStamp[UCAL_JULIAN_DAY] >= (int32_t)kMinimumUserStamp) { 3192 int32_t bestStamp = newestStamp(UCAL_ERA, UCAL_DAY_OF_WEEK_IN_MONTH, kUnset); 3193 bestStamp = newestStamp(UCAL_YEAR_WOY, UCAL_EXTENDED_YEAR, bestStamp); 3194 if (bestStamp <= fStamp[UCAL_JULIAN_DAY]) { 3195 return internalGet(UCAL_JULIAN_DAY); 3196 } 3197 } 3198 3199 UCalendarDateFields bestField = resolveFields(getFieldResolutionTable()); 3200 if (bestField == UCAL_FIELD_COUNT) { 3201 bestField = UCAL_DAY_OF_MONTH; 3202 } 3203 3204 return handleComputeJulianDay(bestField); 3205 } 3206 3207 // ------------------------------------------- 3208 3209 int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField) { 3210 UBool useMonth = (bestField == UCAL_DAY_OF_MONTH || 3211 bestField == UCAL_WEEK_OF_MONTH || 3212 bestField == UCAL_DAY_OF_WEEK_IN_MONTH); 3213 int32_t year; 3214 3215 if (bestField == UCAL_WEEK_OF_YEAR) { 3216 year = internalGet(UCAL_YEAR_WOY, handleGetExtendedYear()); 3217 internalSet(UCAL_EXTENDED_YEAR, year); 3218 } else { 3219 year = handleGetExtendedYear(); 3220 internalSet(UCAL_EXTENDED_YEAR, year); 3221 } 3222 3223 #if defined (U_DEBUG_CAL) 3224 fprintf(stderr, "%s:%d: bestField= %s - y=%d\n", __FILE__, __LINE__, fldName(bestField), year); 3225 #endif 3226 3227 // Get the Julian day of the day BEFORE the start of this year. 3228 // If useMonth is true, get the day before the start of the month. 3229 3230 // give calendar subclass a chance to have a default 'first' month 3231 int32_t month; 3232 3233 if(isSet(UCAL_MONTH)) { 3234 month = internalGet(UCAL_MONTH); 3235 } else { 3236 month = getDefaultMonthInYear(year); 3237 } 3238 3239 int32_t julianDay = handleComputeMonthStart(year, useMonth ? month : 0, useMonth); 3240 3241 if (bestField == UCAL_DAY_OF_MONTH) { 3242 3243 // give calendar subclass a chance to have a default 'first' dom 3244 int32_t dayOfMonth; 3245 if(isSet(UCAL_DAY_OF_MONTH)) { 3246 dayOfMonth = internalGet(UCAL_DAY_OF_MONTH,1); 3247 } else { 3248 dayOfMonth = getDefaultDayInMonth(year, month); 3249 } 3250 return julianDay + dayOfMonth; 3251 } 3252 3253 if (bestField == UCAL_DAY_OF_YEAR) { 3254 return julianDay + internalGet(UCAL_DAY_OF_YEAR); 3255 } 3256 3257 int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw 3258 3259 // At this point julianDay is the 0-based day BEFORE the first day of 3260 // January 1, year 1 of the given calendar. If julianDay == 0, it 3261 // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian 3262 // or Gregorian). (or it is before the month we are in, if useMonth is True) 3263 3264 // At this point we need to process the WEEK_OF_MONTH or 3265 // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH. 3266 // First, perform initial shared computations. These locate the 3267 // first week of the period. 3268 3269 // Get the 0-based localized DOW of day one of the month or year. 3270 // Valid range 0..6. 3271 int32_t first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; 3272 if (first < 0) { 3273 first += 7; 3274 } 3275 3276 int32_t dowLocal = getLocalDOW(); 3277 3278 // Find the first target DOW (dowLocal) in the month or year. 3279 // Actually, it may be just before the first of the month or year. 3280 // It will be an integer from -5..7. 3281 int32_t date = 1 - first + dowLocal; 3282 3283 if (bestField == UCAL_DAY_OF_WEEK_IN_MONTH) { 3284 // Adjust the target DOW to be in the month or year. 3285 if (date < 1) { 3286 date += 7; 3287 } 3288 3289 // The only trickiness occurs if the day-of-week-in-month is 3290 // negative. 3291 int32_t dim = internalGet(UCAL_DAY_OF_WEEK_IN_MONTH, 1); 3292 if (dim >= 0) { 3293 date += 7*(dim - 1); 3294 3295 } else { 3296 // Move date to the last of this day-of-week in this month, 3297 // then back up as needed. If dim==-1, we don't back up at 3298 // all. If dim==-2, we back up once, etc. Don't back up 3299 // past the first of the given day-of-week in this month. 3300 // Note that we handle -2, -3, etc. correctly, even though 3301 // values < -1 are technically disallowed. 3302 int32_t m = internalGet(UCAL_MONTH, UCAL_JANUARY); 3303 int32_t monthLength = handleGetMonthLength(year, m); 3304 date += ((monthLength - date) / 7 + dim + 1) * 7; 3305 } 3306 } else { 3307 #if defined (U_DEBUG_CAL) 3308 fprintf(stderr, "%s:%d - bf= %s\n", __FILE__, __LINE__, fldName(bestField)); 3309 #endif 3310 3311 if(bestField == UCAL_WEEK_OF_YEAR) { // ------------------------------------- WOY ------------- 3312 if(!isSet(UCAL_YEAR_WOY) || // YWOY not set at all or 3313 ( (resolveFields(kYearPrecedence) != UCAL_YEAR_WOY) // YWOY doesn't have precedence 3314 && (fStamp[UCAL_YEAR_WOY]!=kInternallySet) ) ) // (excluding where all fields are internally set - then YWOY is used) 3315 { 3316 // need to be sure to stay in 'real' year. 3317 int32_t woy = internalGet(bestField); 3318 3319 int32_t nextJulianDay = handleComputeMonthStart(year+1, 0, FALSE); // jd of day before jan 1 3320 int32_t nextFirst = julianDayToDayOfWeek(nextJulianDay + 1) - firstDayOfWeek; 3321 3322 if (nextFirst < 0) { // 0..6 ldow of Jan 1 3323 nextFirst += 7; 3324 } 3325 3326 if(woy==1) { // FIRST WEEK --------------------------------- 3327 #if defined (U_DEBUG_CAL) 3328 fprintf(stderr, "%s:%d - woy=%d, yp=%d, nj(%d)=%d, nf=%d", __FILE__, __LINE__, 3329 internalGet(bestField), resolveFields(kYearPrecedence), year+1, 3330 nextJulianDay, nextFirst); 3331 3332 fprintf(stderr, " next: %d DFW, min=%d \n", (7-nextFirst), getMinimalDaysInFirstWeek() ); 3333 #endif 3334 3335 // nextFirst is now the localized DOW of Jan 1 of y-woy+1 3336 if((nextFirst > 0) && // Jan 1 starts on FDOW 3337 (7-nextFirst) >= getMinimalDaysInFirstWeek()) // or enough days in the week 3338 { 3339 // Jan 1 of (yearWoy+1) is in yearWoy+1 - recalculate JD to next year 3340 #if defined (U_DEBUG_CAL) 3341 fprintf(stderr, "%s:%d - was going to move JD from %d to %d [d%d]\n", __FILE__, __LINE__, 3342 julianDay, nextJulianDay, (nextJulianDay-julianDay)); 3343 #endif 3344 julianDay = nextJulianDay; 3345 3346 // recalculate 'first' [0-based local dow of jan 1] 3347 first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; 3348 if (first < 0) { 3349 first += 7; 3350 } 3351 // recalculate date. 3352 date = 1 - first + dowLocal; 3353 } 3354 } else if(woy>=getLeastMaximum(bestField)) { 3355 // could be in the last week- find out if this JD would overstep 3356 int32_t testDate = date; 3357 if ((7 - first) < getMinimalDaysInFirstWeek()) { 3358 testDate += 7; 3359 } 3360 3361 // Now adjust for the week number. 3362 testDate += 7 * (woy - 1); 3363 3364 #if defined (U_DEBUG_CAL) 3365 fprintf(stderr, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n", 3366 __FILE__, __LINE__, year, year-1, testDate, julianDay+testDate, nextJulianDay); 3367 #endif 3368 if(julianDay+testDate > nextJulianDay) { // is it past Dec 31? (nextJulianDay is day BEFORE year+1's Jan 1) 3369 // Fire up the calculating engines.. retry YWOY = (year-1) 3370 julianDay = handleComputeMonthStart(year-1, 0, FALSE); // jd before Jan 1 of previous year 3371 first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; // 0 based local dow of first week 3372 3373 if(first < 0) { // 0..6 3374 first += 7; 3375 } 3376 date = 1 - first + dowLocal; 3377 3378 #if defined (U_DEBUG_CAL) 3379 fprintf(stderr, "%s:%d - date now %d, jd%d, ywoy%d\n", 3380 __FILE__, __LINE__, date, julianDay, year-1); 3381 #endif 3382 3383 3384 } /* correction needed */ 3385 } /* leastmaximum */ 3386 } /* resolvefields(year) != year_woy */ 3387 } /* bestfield != week_of_year */ 3388 3389 // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR) 3390 // Adjust for minimal days in first week 3391 if ((7 - first) < getMinimalDaysInFirstWeek()) { 3392 date += 7; 3393 } 3394 3395 // Now adjust for the week number. 3396 date += 7 * (internalGet(bestField) - 1); 3397 } 3398 3399 return julianDay + date; 3400 } 3401 3402 int32_t 3403 Calendar::getDefaultMonthInYear(int32_t /*eyear*/) 3404 { 3405 return 0; 3406 } 3407 3408 int32_t 3409 Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/) 3410 { 3411 return 1; 3412 } 3413 3414 3415 int32_t Calendar::getLocalDOW() 3416 { 3417 // Get zero-based localized DOW, valid range 0..6. This is the DOW 3418 // we are looking for. 3419 int32_t dowLocal = 0; 3420 switch (resolveFields(kDOWPrecedence)) { 3421 case UCAL_DAY_OF_WEEK: 3422 dowLocal = internalGet(UCAL_DAY_OF_WEEK) - fFirstDayOfWeek; 3423 break; 3424 case UCAL_DOW_LOCAL: 3425 dowLocal = internalGet(UCAL_DOW_LOCAL) - 1; 3426 break; 3427 default: 3428 break; 3429 } 3430 dowLocal = dowLocal % 7; 3431 if (dowLocal < 0) { 3432 dowLocal += 7; 3433 } 3434 return dowLocal; 3435 } 3436 3437 int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy) 3438 { 3439 // We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine 3440 // what year we fall in, so that other code can set it properly. 3441 // (code borrowed from computeWeekFields and handleComputeJulianDay) 3442 //return yearWoy; 3443 3444 // First, we need a reliable DOW. 3445 UCalendarDateFields bestField = resolveFields(kDatePrecedence); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields 3446 3447 // Now, a local DOW 3448 int32_t dowLocal = getLocalDOW(); // 0..6 3449 int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw 3450 int32_t jan1Start = handleComputeMonthStart(yearWoy, 0, FALSE); 3451 int32_t nextJan1Start = handleComputeMonthStart(yearWoy+1, 0, FALSE); // next year's Jan1 start 3452 3453 // At this point julianDay is the 0-based day BEFORE the first day of 3454 // January 1, year 1 of the given calendar. If julianDay == 0, it 3455 // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian 3456 // or Gregorian). (or it is before the month we are in, if useMonth is True) 3457 3458 // At this point we need to process the WEEK_OF_MONTH or 3459 // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH. 3460 // First, perform initial shared computations. These locate the 3461 // first week of the period. 3462 3463