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