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