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