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