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