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