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