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