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