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