2 *******************************************************************************
3 * Copyright (C) 1997-2013, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
9 * Modification History:
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
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 *******************************************************************************
27 #include "utypeinfo.h" // for 'typeid' to work
29 #include "unicode/utypes.h"
31 #if !UCONFIG_NO_FORMATTING
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"
45 #include "indiancal.h"
50 #include "unicode/calendar.h"
61 #if !UCONFIG_NO_SERVICE
62 static icu::ICULocaleService* gService = NULL;
63 static icu::UInitOnce gServiceInitOnce = U_INITONCE_INITIALIZER;
66 // INTERNAL - for cleanup
69 static UBool calendar_cleanup(void) {
70 #if !UCONFIG_NO_SERVICE
75 gServiceInitOnce.reset();
81 // ------------------------------------------
85 //-------------------------------------------
86 //#define U_DEBUG_CALSVC 1
89 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
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)
102 * convert a UCalendarDateFields into a string - for debugging
103 * @param f field enum
104 * @return static string to the field name
108 const char* fldName(UCalendarDateFields f) {
109 return udbg_enumName(UDBG_UCalendarDateFields, (int32_t)f);
113 // from CalendarTest::calToStr - but doesn't modify contents.
114 void ucal_dump(const Calendar &cal) {
118 void Calendar::dump() const {
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',
125 // can add more things here: DST, zone, etc.
126 fprintf(stderr, "\n");
127 for(i = 0;i<UCAL_FIELD_COUNT;i++) {
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) ");
138 fprintf(stderr, " %%%d ", fStamp[i]);
140 fprintf(stderr, "\n");
145 U_CFUNC void ucal_dump(UCalendar* cal) {
146 ucal_dump( *((Calendar*)cal) );
152 /* Max value for stamp allowable before recalculation */
153 #define STAMP_MAX 10000
155 static const char * const gCalTypes[] = {
168 "ethiopic-amete-alem",
177 // Must be in the order of gCalTypes above
178 typedef enum ECalType {
179 CALTYPE_UNKNOWN = -1,
180 CALTYPE_GREGORIAN = 0,
185 CALTYPE_ISLAMIC_CIVIL,
192 CALTYPE_ETHIOPIC_AMETE_ALEM,
195 CALTYPE_ISLAMIC_UMALQURA,
196 CALTYPE_ISLAMIC_TBLA,
202 static ECalType getCalendarType(const char *s) {
203 for (int i = 0; gCalTypes[i] != NULL; i++) {
204 if (uprv_stricmp(s, gCalTypes[i]) == 0) {
208 return CALTYPE_UNKNOWN;
211 static UBool isStandardSupportedKeyword(const char *keyword, UErrorCode& status) {
212 if(U_FAILURE(status)) {
215 ECalType calType = getCalendarType(keyword);
216 return (calType != CALTYPE_UNKNOWN);
219 static void getCalendarKeyword(const UnicodeString &id, char *targetBuffer, int32_t targetBufferSize) {
220 UnicodeString calendarKeyword = UNICODE_STRING_SIMPLE("calendar=");
221 int32_t calKeyLen = calendarKeyword.length();
224 int32_t keywordIdx = id.indexOf((UChar)0x003D); /* '=' */
225 if (id[0] == 0x40/*'@'*/
226 && id.compareBetween(1, keywordIdx+1, calendarKeyword, 0, calKeyLen) == 0)
228 keyLen = id.extract(keywordIdx+1, id.length(), targetBuffer, targetBufferSize, US_INV);
230 targetBuffer[keyLen] = 0;
233 static ECalType getCalendarTypeForLocale(const char *locid) {
234 UErrorCode status = U_ZERO_ERROR;
235 ECalType calType = CALTYPE_UNKNOWN;
237 //TODO: ULOC_FULL_NAME is out of date and too small..
238 char canonicalName[256];
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;
246 canonicalName[canonicalLen] = 0; // terminate
249 int32_t calTypeBufLen;
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) {
259 status = U_ZERO_ERROR;
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) {
268 uloc_addLikelySubtags(locid, fullLoc, sizeof(fullLoc) - 1, &status);
269 regionLen = uloc_getCountry(fullLoc, region, sizeof(region) - 1, &status);
271 if (U_FAILURE(status)) {
272 return CALTYPE_GREGORIAN;
274 region[regionLen] = 0;
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);
286 if (U_SUCCESS(status) && order != NULL) {
287 // the first calender type is the default for the region
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);
300 if (calType == CALTYPE_UNKNOWN) {
302 calType = CALTYPE_GREGORIAN;
307 static Calendar *createStandardCalendar(ECalType calType, const Locale &loc, UErrorCode& status) {
308 Calendar *cal = NULL;
311 case CALTYPE_GREGORIAN:
312 cal = new GregorianCalendar(loc, status);
314 case CALTYPE_JAPANESE:
315 cal = new JapaneseCalendar(loc, status);
317 case CALTYPE_BUDDHIST:
318 cal = new BuddhistCalendar(loc, status);
321 cal = new TaiwanCalendar(loc, status);
323 case CALTYPE_PERSIAN:
324 cal = new PersianCalendar(loc, status);
326 case CALTYPE_ISLAMIC_TBLA:
327 cal = new IslamicCalendar(loc, status, IslamicCalendar::TBLA);
329 case CALTYPE_ISLAMIC_CIVIL:
330 cal = new IslamicCalendar(loc, status, IslamicCalendar::CIVIL);
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);
337 case CALTYPE_ISLAMIC_UMALQURA:
338 cal = new IslamicCalendar(loc, status, IslamicCalendar::UMALQURA);
341 cal = new HebrewCalendar(loc, status);
343 case CALTYPE_CHINESE:
344 cal = new ChineseCalendar(loc, status);
347 cal = new IndianCalendar(loc, status);
350 cal = new CopticCalendar(loc, status);
352 case CALTYPE_ETHIOPIC:
353 cal = new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_MIHRET_ERA);
355 case CALTYPE_ETHIOPIC_AMETE_ALEM:
356 cal = new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_ALEM_ERA);
358 case CALTYPE_ISO8601:
359 cal = new GregorianCalendar(loc, status);
360 cal->setFirstDayOfWeek(UCAL_MONDAY);
361 cal->setMinimalDaysInFirstWeek(4);
364 cal = new DangiCalendar(loc, status);
367 status = U_UNSUPPORTED_ERROR;
373 #if !UCONFIG_NO_SERVICE
375 // -------------------------------------
378 * a Calendar Factory which creates the "basic" calendar types, that is, those
381 class BasicCalendarFactory : public LocaleKeyFactory {
384 * @param calendarType static const string (caller owns storage - will be aliased) to calendar type
386 BasicCalendarFactory()
387 : LocaleKeyFactory(LocaleKeyFactory::INVISIBLE) { }
389 virtual ~BasicCalendarFactory();
392 //virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const {
393 // if(U_FAILURE(status)) {
396 // char keyword[ULOC_FULLNAME_CAPACITY];
397 // getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword));
398 // return isStandardSupportedKeyword(keyword, status);
401 virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const
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);
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");
419 const LocaleKey& lkey = (LocaleKey&)key;
420 Locale curLoc; // current locale
421 Locale canLoc; // Canonical locale
423 lkey.currentLocale(curLoc);
424 lkey.canonicalLocale(canLoc);
426 char keyword[ULOC_FULLNAME_CAPACITY];
430 getCalendarKeyword(str, keyword, (int32_t) sizeof(keyword));
432 #ifdef U_DEBUG_CALSVC
433 fprintf(stderr, "BasicCalendarFactory::create() - cur %s, can %s\n", (const char*)curLoc.getName(), (const char*)canLoc.getName());
436 if(!isStandardSupportedKeyword(keyword,status)) { // Do we handle this type?
437 #ifdef U_DEBUG_CALSVC
439 fprintf(stderr, "BasicCalendarFactory - not handling %s.[%s]\n", (const char*) curLoc.getName(), tmp );
444 return createStandardCalendar(getCalendarType(keyword), canLoc, status);
448 BasicCalendarFactory::~BasicCalendarFactory() {}
451 * A factory which looks up the DefaultCalendar resource to determine which class of calendar to use
454 class DefaultCalendarFactory : public ICUResourceBundleFactory {
456 DefaultCalendarFactory() : ICUResourceBundleFactory() { }
457 virtual ~DefaultCalendarFactory();
459 virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const {
461 LocaleKey &lkey = (LocaleKey&)key;
463 lkey.currentLocale(loc);
465 UnicodeString *ret = new UnicodeString();
467 status = U_MEMORY_ALLOCATION_ERROR;
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));
477 DefaultCalendarFactory::~DefaultCalendarFactory() {}
479 // -------------------------------------
480 class CalendarService : public ICULocaleService {
483 : ICULocaleService(UNICODE_STRING_SIMPLE("Calendar"))
485 UErrorCode status = U_ZERO_ERROR;
486 registerFactory(new DefaultCalendarFactory(), status);
489 virtual ~CalendarService();
491 virtual UObject* cloneInstance(UObject* instance) const {
492 UnicodeString *s = dynamic_cast<UnicodeString *>(instance);
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));
500 return ((Calendar*)instance)->clone();
504 virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /*actualID*/, UErrorCode& status) const {
505 LocaleKey& lkey = (LocaleKey&)key;
506 //int32_t kind = lkey.kind();
509 lkey.canonicalLocale(loc);
511 #ifdef U_DEBUG_CALSVC
513 lkey.currentLocale(loc2);
514 fprintf(stderr, "CalSvc:handleDefault for currentLoc %s, canloc %s\n", (const char*)loc.getName(), (const char*)loc2.getName());
516 Calendar *nc = new GregorianCalendar(loc, status);
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));
525 virtual UBool isDefault() const {
526 return countFactories() == 1;
530 CalendarService::~CalendarService() {}
532 // -------------------------------------
535 isCalendarServiceUsed() {
536 return !gServiceInitOnce.isReset();
539 // -------------------------------------
541 static void U_CALLCONV
542 initCalendarService(UErrorCode &status)
544 #ifdef U_DEBUG_CALSVC
545 fprintf(stderr, "Spinning up Calendar Service\n");
547 ucln_i18n_registerCleanup(UCLN_I18N_CALENDAR, calendar_cleanup);
548 gService = new CalendarService();
549 if (gService == NULL) {
550 status = U_MEMORY_ALLOCATION_ERROR;
553 #ifdef U_DEBUG_CALSVC
554 fprintf(stderr, "Registering classes..\n");
557 // Register all basic instances.
558 gService->registerFactory(new BasicCalendarFactory(),status);
560 #ifdef U_DEBUG_CALSVC
561 fprintf(stderr, "Done..\n");
564 if(U_FAILURE(status)) {
565 #ifdef U_DEBUG_CALSVC
566 fprintf(stderr, "err (%s) registering classes, deleting service.....\n", u_errorName(status));
573 static ICULocaleService*
574 getCalendarService(UErrorCode &status)
576 umtx_initOnce(gServiceInitOnce, &initCalendarService, status);
580 URegistryKey Calendar::registerFactory(ICUServiceFactory* toAdopt, UErrorCode& status)
582 return getCalendarService(status)->registerFactory(toAdopt, status);
585 UBool Calendar::unregister(URegistryKey key, UErrorCode& status) {
586 return getCalendarService(status)->unregister(key, status);
588 #endif /* UCONFIG_NO_SERVICE */
590 // -------------------------------------
592 static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = {
593 // Minimum Greatest min Least max Greatest max
594 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // ERA
595 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR
596 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // MONTH
597 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_YEAR
598 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_MONTH
599 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_MONTH
600 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_YEAR
601 { 1, 1, 7, 7 }, // DAY_OF_WEEK
602 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_WEEK_IN_MONTH
603 { 0, 0, 1, 1 }, // AM_PM
604 { 0, 0, 11, 11 }, // HOUR
605 { 0, 0, 23, 23 }, // HOUR_OF_DAY
606 { 0, 0, 59, 59 }, // MINUTE
607 { 0, 0, 59, 59 }, // SECOND
608 { 0, 0, 999, 999 }, // MILLISECOND
609 {-12*kOneHour, -12*kOneHour, 12*kOneHour, 15*kOneHour }, // ZONE_OFFSET
610 { 0, 0, 1*kOneHour, 1*kOneHour }, // DST_OFFSET
611 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR_WOY
612 { 1, 1, 7, 7 }, // DOW_LOCAL
613 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // EXTENDED_YEAR
614 { -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY
615 { 0, 0, 24*kOneHour-1, 24*kOneHour-1 }, // MILLISECONDS_IN_DAY
616 { 0, 0, 1, 1 }, // IS_LEAP_MONTH
619 // Resource bundle tags read by this class
620 static const char gMonthNames[] = "monthNames";
622 // Data flow in Calendar
623 // ---------------------
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.
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
641 // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
643 // | Using Calendar-specific algorithm
645 // local standard millis
647 // | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET
649 // UTC millis (in time data member)
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()
655 // UTC millis (in time data member)
657 // | Using TimeZone getOffset()
659 // local standard millis
661 // | Using Calendar-specific algorithm
663 // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
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.
672 // -------------------------------------
674 Calendar::Calendar(UErrorCode& success)
677 fAreFieldsSet(FALSE),
678 fAreAllFieldsSet(FALSE),
679 fAreFieldsVirtuallySet(FALSE),
680 fNextStamp((int32_t)kMinimumUserStamp),
684 fRepeatedWallTime(UCAL_WALLTIME_LAST),
685 fSkippedWallTime(UCAL_WALLTIME_LAST)
688 fZone = TimeZone::createDefault();
690 success = U_MEMORY_ALLOCATION_ERROR;
692 setWeekData(Locale::getDefault(), NULL, success);
695 // -------------------------------------
697 Calendar::Calendar(TimeZone* zone, const Locale& aLocale, UErrorCode& success)
700 fAreFieldsSet(FALSE),
701 fAreAllFieldsSet(FALSE),
702 fAreFieldsVirtuallySet(FALSE),
703 fNextStamp((int32_t)kMinimumUserStamp),
707 fRepeatedWallTime(UCAL_WALLTIME_LAST),
708 fSkippedWallTime(UCAL_WALLTIME_LAST)
711 #if defined (U_DEBUG_CAL)
712 fprintf(stderr, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n",
715 success = U_ILLEGAL_ARGUMENT_ERROR;
722 setWeekData(aLocale, NULL, success);
725 // -------------------------------------
727 Calendar::Calendar(const TimeZone& zone, const Locale& aLocale, UErrorCode& success)
730 fAreFieldsSet(FALSE),
731 fAreAllFieldsSet(FALSE),
732 fAreFieldsVirtuallySet(FALSE),
733 fNextStamp((int32_t)kMinimumUserStamp),
737 fRepeatedWallTime(UCAL_WALLTIME_LAST),
738 fSkippedWallTime(UCAL_WALLTIME_LAST)
741 fZone = zone.clone();
743 success = U_MEMORY_ALLOCATION_ERROR;
745 setWeekData(aLocale, NULL, success);
748 // -------------------------------------
750 Calendar::~Calendar()
755 // -------------------------------------
757 Calendar::Calendar(const Calendar &source)
764 // -------------------------------------
767 Calendar::operator=(const Calendar &right)
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);
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;
784 if (right.fZone != NULL) {
785 fZone = right.fZone->clone();
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);
801 // -------------------------------------
804 Calendar::createInstance(UErrorCode& success)
806 return createInstance(TimeZone::createDefault(), Locale::getDefault(), success);
809 // -------------------------------------
812 Calendar::createInstance(const TimeZone& zone, UErrorCode& success)
814 return createInstance(zone, Locale::getDefault(), success);
817 // -------------------------------------
820 Calendar::createInstance(const Locale& aLocale, UErrorCode& success)
822 return createInstance(TimeZone::createDefault(), aLocale, success);
825 // ------------------------------------- Adopting
827 // Note: this is the bottleneck that actually calls the service routines.
830 Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success)
832 if (U_FAILURE(success)) {
839 #if !UCONFIG_NO_SERVICE
840 if (isCalendarServiceUsed()) {
841 u = getCalendarService(success)->get(aLocale, LocaleKey::KIND_ANY, &actualLoc, success);
846 u = createStandardCalendar(getCalendarTypeForLocale(aLocale.getName()), aLocale, success);
850 if(U_FAILURE(success) || !u) {
852 if(U_SUCCESS(success)) { // Propagate some kind of err
853 success = U_INTERNAL_PROGRAM_ERROR;
858 #if !UCONFIG_NO_SERVICE
859 const UnicodeString* str = dynamic_cast<const UnicodeString*>(u);
861 // It's a unicode string telling us what type of calendar to load ("gregorian", etc)
862 // Create a Locale over this string
864 LocaleUtility::initLocaleFromName(*str, l);
866 #ifdef U_DEBUG_CALSVC
867 fprintf(stderr, "Calendar::createInstance(%s), looking up [%s]\n", aLocale.getName(), l.getName());
874 // Don't overwrite actualLoc, since the actual loc from this call
875 // may be something like "@calendar=gregorian" -- TODO investigate
877 c = (Calendar*)getCalendarService(success)->get(l, LocaleKey::KIND_ANY, &actualLoc2, success);
879 if(U_FAILURE(success) || !c) {
881 if(U_SUCCESS(success)) {
882 success = U_INTERNAL_PROGRAM_ERROR; // Propagate some err
887 str = dynamic_cast<const UnicodeString*>(c);
889 // recursed! Second lookup returned a UnicodeString.
890 // Perhaps DefaultCalendar{} was set to another locale.
891 #ifdef U_DEBUG_CALSVC
893 // Extract a char* out of it..
894 int32_t len = str->length();
895 int32_t actLen = sizeof(tmp)-1;
899 str->extract(0,len,tmp);
902 fprintf(stderr, "err - recursed, 2nd lookup was unistring %s\n", tmp);
904 success = U_MISSING_RESOURCE_ERROR; // requested a calendar type which could NOT be found.
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());
912 c->setWeekData(aLocale, c->getType(), success); // set the correct locale (this was an indirected calendar)
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);
923 #endif /* UCONFIG_NO_SERVICE */
925 // a calendar was returned - we assume the factory did the right thing.
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.
936 // -------------------------------------
939 Calendar::createInstance(const TimeZone& zone, const Locale& aLocale, UErrorCode& success)
941 Calendar* c = createInstance(aLocale, success);
942 if(U_SUCCESS(success) && c) {
943 c->setTimeZone(zone);
948 // -------------------------------------
951 Calendar::operator==(const Calendar& that) const
953 UErrorCode status = U_ZERO_ERROR;
954 return isEquivalentTo(that) &&
955 getTimeInMillis(status) == that.getTimeInMillis(status) &&
960 Calendar::isEquivalentTo(const Calendar& other) const
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;
975 // -------------------------------------
978 Calendar::equals(const Calendar& when, UErrorCode& status) const
980 return (this == &when ||
981 getTime(status) == when.getTime(status));
984 // -------------------------------------
987 Calendar::before(const Calendar& when, UErrorCode& status) const
989 return (this != &when &&
990 getTimeInMillis(status) < when.getTimeInMillis(status));
993 // -------------------------------------
996 Calendar::after(const Calendar& when, UErrorCode& status) const
998 return (this != &when &&
999 getTimeInMillis(status) > when.getTimeInMillis(status));
1002 // -------------------------------------
1005 const Locale* U_EXPORT2
1006 Calendar::getAvailableLocales(int32_t& count)
1008 return Locale::getAvailableLocales(count);
1011 // -------------------------------------
1013 StringEnumeration* U_EXPORT2
1014 Calendar::getKeywordValuesForLocale(const char* key,
1015 const Locale& locale, UBool commonlyUsed, UErrorCode& status)
1017 // This is a wrapper over ucal_getKeywordValuesForLocale
1018 UEnumeration *uenum = ucal_getKeywordValuesForLocale(key, locale.getName(),
1019 commonlyUsed, &status);
1020 if (U_FAILURE(status)) {
1024 return new UStringEnumeration(uenum);
1027 // -------------------------------------
1032 return uprv_getUTCtime(); // return as milliseconds
1035 // -------------------------------------
1038 * Gets this Calendar's current time as a long.
1039 * @return the current time as UTC milliseconds from the epoch.
1042 Calendar::getTimeInMillis(UErrorCode& status) const
1044 if(U_FAILURE(status))
1048 ((Calendar*)this)->updateTime(status);
1050 /* Test for buffer overflows */
1051 if(U_FAILURE(status)) {
1057 // -------------------------------------
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.
1067 Calendar::setTimeInMillis( double millis, UErrorCode& status ) {
1068 if(U_FAILURE(status))
1071 if (millis > MAX_MILLIS) {
1073 millis = MAX_MILLIS;
1075 status = U_ILLEGAL_ARGUMENT_ERROR;
1078 } else if (millis < MIN_MILLIS) {
1080 millis = MIN_MILLIS;
1082 status = U_ILLEGAL_ARGUMENT_ERROR;
1088 fAreFieldsSet = fAreAllFieldsSet = FALSE;
1089 fIsTimeSet = fAreFieldsVirtuallySet = TRUE;
1091 for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
1100 // -------------------------------------
1103 Calendar::get(UCalendarDateFields field, UErrorCode& status) const
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
1108 if (U_SUCCESS(status)) ((Calendar*)this)->complete(status); // Cast away const
1109 return U_SUCCESS(status) ? fFields[field] : 0;
1112 // -------------------------------------
1115 Calendar::set(UCalendarDateFields field, int32_t value)
1117 if (fAreFieldsVirtuallySet) {
1118 UErrorCode ec = U_ZERO_ERROR;
1121 fFields[field] = value;
1122 /* Ensure that the fNextStamp value doesn't go pass max value for int32_t */
1123 if (fNextStamp == STAMP_MAX) {
1126 fStamp[field] = fNextStamp++;
1127 fIsSet[field] = TRUE; // Remove later
1128 fIsTimeSet = fAreFieldsSet = fAreFieldsVirtuallySet = FALSE;
1131 // -------------------------------------
1134 Calendar::set(int32_t year, int32_t month, int32_t date)
1136 set(UCAL_YEAR, year);
1137 set(UCAL_MONTH, month);
1138 set(UCAL_DATE, date);
1141 // -------------------------------------
1144 Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute)
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);
1153 // -------------------------------------
1156 Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute, int32_t second)
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);
1166 // -------------------------------------
1171 for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
1172 fFields[i] = 0; // Must do this; other code depends on it
1174 fIsSet[i] = FALSE; // Remove later
1176 fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE;
1177 // fTime is not 'cleared' - may be used if no fields are set.
1180 // -------------------------------------
1183 Calendar::clear(UCalendarDateFields field)
1185 if (fAreFieldsVirtuallySet) {
1186 UErrorCode ec = U_ZERO_ERROR;
1190 fStamp[field] = kUnset;
1191 fIsSet[field] = FALSE; // Remove later
1192 fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE;
1195 // -------------------------------------
1198 Calendar::isSet(UCalendarDateFields field) const
1200 return fAreFieldsVirtuallySet || (fStamp[field] != kUnset);
1204 int32_t Calendar::newestStamp(UCalendarDateFields first, UCalendarDateFields last, int32_t bestStampSoFar) const
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];
1216 // -------------------------------------
1219 Calendar::complete(UErrorCode& status)
1223 /* Test for buffer overflows */
1224 if(U_FAILURE(status)) {
1228 if (!fAreFieldsSet) {
1229 computeFields(status); // fills in unset fields
1230 /* Test for buffer overflows */
1231 if(U_FAILURE(status)) {
1234 fAreFieldsSet = TRUE;
1235 fAreAllFieldsSet = TRUE;
1239 //-------------------------------------------------------------------------
1240 // Protected utility methods for use by subclasses. These are very handy
1241 // for implementing add, roll, and computeFields.
1242 //-------------------------------------------------------------------------
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
1251 * <b>Subclassing:</b>
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}.
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.
1265 * @param field The calendar field whose value should be pinned.
1267 * @see #getActualMinimum
1268 * @see #getActualMaximum
1271 void Calendar::pinField(UCalendarDateFields field, UErrorCode& status) {
1272 int32_t max = getActualMaximum(field, status);
1273 int32_t min = getActualMinimum(field, status);
1275 if (fFields[field] > max) {
1277 } else if (fFields[field] < min) {
1283 void Calendar::computeFields(UErrorCode &ec)
1285 if (U_FAILURE(ec)) {
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);
1294 // Mark fields as set. Do this before calling handleComputeFields().
1295 uint32_t mask = //fInternalSetMask;
1299 (1 << UCAL_DAY_OF_MONTH) | // = UCAL_DATE
1300 (1 << UCAL_DAY_OF_YEAR) |
1301 (1 << UCAL_EXTENDED_YEAR);
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
1309 fIsSet[i] = FALSE; // Remove later
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
1323 int32_t days = (int32_t)ClockMath::floorDivide(localMillis, (double)kOneDay);
1325 internalSet(UCAL_JULIAN_DAY,days + kEpochStartAsJulianDay);
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);
1332 computeGregorianAndDOWFields(fFields[UCAL_JULIAN_DAY], ec);
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);
1340 // Compute week-related fields, based on the subclass-computed
1341 // fields computed by handleComputeFields().
1342 computeWeekFields(ec);
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;
1353 fFields[UCAL_MINUTE] = 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;
1362 uint8_t Calendar::julianDayToDayOfWeek(double julian)
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);
1368 uint8_t result = (uint8_t)(dayOfWeek + ((dayOfWeek < 0) ? (7+UCAL_SUNDAY ) : UCAL_SUNDAY));
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
1378 void Calendar::computeGregorianAndDOWFields(int32_t julianDay, UErrorCode &ec)
1380 computeGregorianFields(julianDay, ec);
1382 // Compute day of week: JD 0 = Monday
1383 int32_t dow = julianDayToDayOfWeek(julianDay);
1384 internalSet(UCAL_DAY_OF_WEEK,dow);
1386 // Calculate 1-based localized day of week
1387 int32_t dowLocal = dow - getFirstDayOfWeek() + 1;
1391 internalSet(UCAL_DOW_LOCAL,dowLocal);
1392 fFields[UCAL_DOW_LOCAL] = dowLocal;
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.
1402 void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode & /* ec */) {
1403 int32_t gregorianDayOfWeekUnused;
1404 Grego::dayToFields(julianDay - kEpochStartAsJulianDay, fGregorianYear, fGregorianMonth, fGregorianDayOfMonth, gregorianDayOfWeekUnused, fGregorianDayOfYear);
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.
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.
1427 void Calendar::computeWeekFields(UErrorCode &ec) {
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];
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
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()) {
1452 // Adjust for weeks at the year end that overlap into the previous or
1453 // next calendar year.
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
1460 int32_t prevDoy = dayOfYear + handleGetYearLength(eyear - 1);
1461 woy = weekNumber(prevDoy, dayOfWeek);
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:
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) {
1476 if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) &&
1477 ((dayOfYear + 7 - relDow) > lastDoy)) {
1483 fFields[UCAL_WEEK_OF_YEAR] = woy;
1484 fFields[UCAL_YEAR_WOY] = yearOfWeekOfYear;
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);
1497 int32_t Calendar::weekNumber(int32_t desiredDay, int32_t dayOfPeriod, int32_t dayOfWeek)
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;
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;
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;
1518 void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode &/* status */)
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;
1527 era = GregorianCalendar::BC;
1530 internalSet(UCAL_ERA, era);
1531 internalSet(UCAL_YEAR, eyear);
1533 // -------------------------------------
1536 void Calendar::roll(EDateFields field, int32_t amount, UErrorCode& status)
1538 roll((UCalendarDateFields)field, amount, status);
1541 void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status)
1544 return; // Nothing to do
1549 if(U_FAILURE(status)) {
1553 case UCAL_DAY_OF_MONTH:
1557 case UCAL_MILLISECOND:
1558 case UCAL_MILLISECONDS_IN_DAY:
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.
1564 int32_t min = getActualMinimum(field,status);
1565 int32_t max = getActualMaximum(field,status);
1566 int32_t gap = max - min + 1;
1568 int32_t value = internalGet(field) + amount;
1569 value = (value - min) % gap;
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.
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);
1597 setTimeInMillis(start + kOneHour * (newHour - oldHour),status);
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>.
1607 int32_t max = getActualMaximum(UCAL_MONTH, status);
1608 int32_t mon = (internalGet(UCAL_MONTH) + amount) % (max+1);
1613 set(UCAL_MONTH, mon);
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 ->
1618 pinField(UCAL_DAY_OF_MONTH,status);
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);
1631 const char * calType = getType();
1632 if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) {
1634 era0WithYearsThatGoBackwards = TRUE;
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
1643 newYear = maxYear - ((-newYear) % maxYear);
1644 } else if (newYear > maxYear) {
1645 newYear = ((newYear - 1) % maxYear) + 1;
1647 // else era is unbounded, just pin low year instead of wrapping
1648 } else if (newYear < 1) {
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) {
1657 set(field, newYear);
1658 pinField(UCAL_MONTH,status);
1659 pinField(UCAL_DAY_OF_MONTH,status);
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);
1670 case UCAL_WEEK_OF_MONTH:
1672 // This is tricky, because during the roll we may have to shift
1673 // to a different day of the week. For example:
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.
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
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?
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;
1704 // Find the day of the week (normalized for locale) for the first
1706 int32_t fdm = (dow - internalGet(UCAL_DAY_OF_MONTH) + 1) % 7;
1707 if (fdm < 0) fdm += 7;
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).
1714 if ((7 - fdm) < getMinimalDaysInFirstWeek())
1715 start = 8 - fdm; // Skip the first partial week
1717 start = 1 - fdm; // This may be zero or negative
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.
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;
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 -
1735 if (day_of_month < 0) day_of_month += gap;
1736 day_of_month += start;
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;
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
1748 set(UCAL_DAY_OF_MONTH, day_of_month);
1751 case UCAL_WEEK_OF_YEAR:
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.
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;
1762 // Find the day of the week (normalized for locale) for the first
1764 int32_t fdy = (dow - internalGet(UCAL_DAY_OF_YEAR) + 1) % 7;
1765 if (fdy < 0) fdy += 7;
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).
1772 if ((7 - fdy) < getMinimalDaysInFirstWeek())
1773 start = 8 - fdy; // Skip the first partial week
1775 start = 1 - fdy; // This may be zero or negative
1777 // Get the day of the week (normalized for locale) for the last
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.
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;
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 -
1793 if (day_of_year < 0) day_of_year += gap;
1794 day_of_year += start;
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;
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);
1808 case UCAL_DAY_OF_YEAR:
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;
1815 min2 = internalGetTime() - min2;
1817 // double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay;
1820 double yearLength = getActualMaximum(UCAL_DAY_OF_YEAR,status);
1821 double oneYear = yearLength;
1823 newtime = uprv_fmod((internalGetTime() + delta - min2), oneYear);
1824 if (newtime < 0) newtime += oneYear;
1825 setTimeInMillis(newtime + min2, status);
1828 case UCAL_DAY_OF_WEEK:
1829 case UCAL_DOW_LOCAL:
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);
1846 case UCAL_DAY_OF_WEEK_IN_MONTH:
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
1854 int32_t preWeeks = (internalGet(UCAL_DAY_OF_MONTH) - 1) / 7;
1855 // Find the number of same days of the week after this one
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);
1868 case UCAL_JULIAN_DAY:
1869 set(field, internalGet(field) + amount);
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));
1877 status = U_ILLEGAL_ARGUMENT_ERROR;
1881 void Calendar::add(EDateFields field, int32_t amount, UErrorCode& status)
1883 Calendar::add((UCalendarDateFields)field, amount, status);
1886 // -------------------------------------
1887 void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status)
1890 return; // Do nothing!
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.
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.
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>.
1914 double delta = amount; // delta in ms
1915 UBool keepHourInvariant = TRUE;
1919 set(field, get(field, status) + amount);
1920 pinField(UCAL_ERA, status);
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);
1935 const char * calType = getType();
1936 if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) {
1941 // Fall through into normal handling
1942 case UCAL_EXTENDED_YEAR:
1945 UBool oldLenient = isLenient();
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);
1956 case UCAL_WEEK_OF_YEAR:
1957 case UCAL_WEEK_OF_MONTH:
1958 case UCAL_DAY_OF_WEEK_IN_MONTH:
1963 delta *= 12 * kOneHour;
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:
1974 case UCAL_HOUR_OF_DAY:
1977 keepHourInvariant = FALSE;
1981 delta *= kOneMinute;
1982 keepHourInvariant = FALSE;
1986 delta *= kOneSecond;
1987 keepHourInvariant = FALSE;
1990 case UCAL_MILLISECOND:
1991 case UCAL_MILLISECONDS_IN_DAY:
1992 keepHourInvariant = FALSE;
1996 #if defined (U_DEBUG_CAL)
1997 fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s not addable",
1998 __FILE__, __LINE__, fldName(field));
2000 status = U_ILLEGAL_ARGUMENT_ERROR;
2002 // throw new IllegalArgumentException("Calendar.add(" + fieldName(field) +
2003 // ") not supported");
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
2010 int32_t prevOffset = 0;
2012 if (keepHourInvariant) {
2013 prevOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status);
2014 hour = internalGet(UCAL_HOUR_OF_DAY);
2017 setTimeInMillis(getTimeInMillis(status) + delta, status);
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.
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);
2047 // -------------------------------------
2048 int32_t Calendar::fieldDifference(UDate when, EDateFields field, UErrorCode& status) {
2049 return fieldDifference(when, (UCalendarDateFields) field, status);
2052 int32_t Calendar::fieldDifference(UDate targetMs, UCalendarDateFields field, UErrorCode& ec) {
2053 if (U_FAILURE(ec)) return 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) {
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) {
2071 } else if (ms > targetMs) {
2073 } else if (max < INT32_MAX) {
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));
2085 ec = U_ILLEGAL_ARGUMENT_ERROR;
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);
2093 double ms = getTimeInMillis(ec);
2094 if (ms == targetMs) {
2096 } else if (ms > targetMs) {
2102 } else if (startMs > targetMs) {
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) {
2111 } else if (ms < targetMs) {
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));
2122 ec = U_ILLEGAL_ARGUMENT_ERROR;
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);
2131 double ms = getTimeInMillis(ec);
2132 if (ms == targetMs) {
2134 } else if (ms < targetMs) {
2141 // Set calendar to end point
2142 setTimeInMillis(startMs, ec);
2143 add(field, min, ec);
2145 /* Test for buffer overflows */
2152 // -------------------------------------
2155 Calendar::adoptTimeZone(TimeZone* zone)
2157 // Do nothing if passed-in zone is NULL
2158 if (zone == NULL) return;
2160 // fZone should always be non-null
2161 if (fZone != NULL) delete fZone;
2164 // if the zone changes, we need to recompute the time fields
2165 fAreFieldsSet = FALSE;
2168 // -------------------------------------
2170 Calendar::setTimeZone(const TimeZone& zone)
2172 adoptTimeZone(zone.clone());
2175 // -------------------------------------
2178 Calendar::getTimeZone() const
2183 // -------------------------------------
2186 Calendar::orphanTimeZone()
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();
2194 // -------------------------------------
2197 Calendar::setLenient(UBool lenient)
2202 // -------------------------------------
2205 Calendar::isLenient() const
2210 // -------------------------------------
2213 Calendar::setRepeatedWallTimeOption(UCalendarWallTimeOption option)
2215 if (option == UCAL_WALLTIME_LAST || option == UCAL_WALLTIME_FIRST) {
2216 fRepeatedWallTime = option;
2220 // -------------------------------------
2222 UCalendarWallTimeOption
2223 Calendar::getRepeatedWallTimeOption(void) const
2225 return fRepeatedWallTime;
2228 // -------------------------------------
2231 Calendar::setSkippedWallTimeOption(UCalendarWallTimeOption option)
2233 fSkippedWallTime = option;
2236 // -------------------------------------
2238 UCalendarWallTimeOption
2239 Calendar::getSkippedWallTimeOption(void) const
2241 return fSkippedWallTime;
2244 // -------------------------------------
2247 Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value)
2249 if (fFirstDayOfWeek != value &&
2250 value >= UCAL_SUNDAY && value <= UCAL_SATURDAY) {
2251 fFirstDayOfWeek = value;
2252 fAreFieldsSet = FALSE;
2256 // -------------------------------------
2258 Calendar::EDaysOfWeek
2259 Calendar::getFirstDayOfWeek() const
2261 return (Calendar::EDaysOfWeek)fFirstDayOfWeek;
2265 Calendar::getFirstDayOfWeek(UErrorCode & /*status*/) const
2267 return fFirstDayOfWeek;
2269 // -------------------------------------
2272 Calendar::setMinimalDaysInFirstWeek(uint8_t value)
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.
2279 } else if (value > 7) {
2282 if (fMinimalDaysInFirstWeek != value) {
2283 fMinimalDaysInFirstWeek = value;
2284 fAreFieldsSet = FALSE;
2288 // -------------------------------------
2291 Calendar::getMinimalDaysInFirstWeek() const
2293 return fMinimalDaysInFirstWeek;
2296 // -------------------------------------
2297 // weekend functions, just dummy implementations for now (for API freeze)
2299 UCalendarWeekdayType
2300 Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const
2302 if (U_FAILURE(status)) {
2303 return UCAL_WEEKDAY;
2305 if (dayOfWeek < UCAL_SUNDAY || dayOfWeek > UCAL_SATURDAY) {
2306 status = U_ILLEGAL_ARGUMENT_ERROR;
2307 return UCAL_WEEKDAY;
2309 if (fWeekendOnset == fWeekendCease) {
2310 if (dayOfWeek != fWeekendOnset)
2311 return UCAL_WEEKDAY;
2312 return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET;
2314 if (fWeekendOnset < fWeekendCease) {
2315 if (dayOfWeek < fWeekendOnset || dayOfWeek > fWeekendCease) {
2316 return UCAL_WEEKDAY;
2319 if (dayOfWeek > fWeekendCease && dayOfWeek < fWeekendOnset) {
2320 return UCAL_WEEKDAY;
2323 if (dayOfWeek == fWeekendOnset) {
2324 return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET;
2326 if (dayOfWeek == fWeekendCease) {
2327 return (fWeekendCeaseMillis >= 86400000) ? UCAL_WEEKEND : UCAL_WEEKEND_CEASE;
2329 return UCAL_WEEKEND;
2333 Calendar::getWeekendTransition(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const
2335 if (U_FAILURE(status)) {
2338 if (dayOfWeek == fWeekendOnset) {
2339 return fWeekendOnsetMillis;
2340 } else if (dayOfWeek == fWeekendCease) {
2341 return fWeekendCeaseMillis;
2343 status = U_ILLEGAL_ARGUMENT_ERROR;
2348 Calendar::isWeekend(UDate date, UErrorCode &status) const
2350 if (U_FAILURE(status)) {
2353 // clone the calendar so we don't mess with the real one.
2354 Calendar *work = (Calendar*)this->clone();
2356 status = U_MEMORY_ALLOCATION_ERROR;
2359 UBool result = FALSE;
2360 work->setTime(date, status);
2361 if (U_SUCCESS(status)) {
2362 result = work->isWeekend();
2369 Calendar::isWeekend(void) const
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)) {
2380 case UCAL_WEEKEND_ONSET:
2381 case UCAL_WEEKEND_CEASE:
2382 // Use internalGet() because the above call to get() populated all fields.
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);
2391 // else fall through, return FALSE
2400 // ------------------------------------- limits
2403 Calendar::getMinimum(EDateFields field) const {
2404 return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MINIMUM);
2408 Calendar::getMinimum(UCalendarDateFields field) const
2410 return getLimit(field,UCAL_LIMIT_MINIMUM);
2413 // -------------------------------------
2415 Calendar::getMaximum(EDateFields field) const
2417 return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MAXIMUM);
2421 Calendar::getMaximum(UCalendarDateFields field) const
2423 return getLimit(field,UCAL_LIMIT_MAXIMUM);
2426 // -------------------------------------
2428 Calendar::getGreatestMinimum(EDateFields field) const
2430 return getLimit((UCalendarDateFields)field,UCAL_LIMIT_GREATEST_MINIMUM);
2434 Calendar::getGreatestMinimum(UCalendarDateFields field) const
2436 return getLimit(field,UCAL_LIMIT_GREATEST_MINIMUM);
2439 // -------------------------------------
2441 Calendar::getLeastMaximum(EDateFields field) const
2443 return getLimit((UCalendarDateFields) field,UCAL_LIMIT_LEAST_MAXIMUM);
2447 Calendar::getLeastMaximum(UCalendarDateFields field) const
2449 return getLimit( field,UCAL_LIMIT_LEAST_MAXIMUM);
2452 // -------------------------------------
2454 Calendar::getActualMinimum(EDateFields field, UErrorCode& status) const
2456 return getActualMinimum((UCalendarDateFields) field, status);
2459 int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) const {
2461 case UCAL_DAY_OF_WEEK:
2464 case UCAL_HOUR_OF_DAY:
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];
2476 case UCAL_WEEK_OF_MONTH:
2479 if (limitType == UCAL_LIMIT_MINIMUM) {
2480 limit = getMinimalDaysInFirstWeek() == 1 ? 1 : 0;
2481 } else if (limitType == UCAL_LIMIT_GREATEST_MINIMUM) {
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;
2495 return handleGetLimit(field, limitType);
2501 Calendar::getActualMinimum(UCalendarDateFields field, UErrorCode& status) const
2503 int32_t fieldValue = getGreatestMinimum(field);
2504 int32_t endValue = getMinimum(field);
2506 // if we know that the minimum value is always the same, just return it
2507 if (fieldValue == endValue) {
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();
2515 status = U_MEMORY_ALLOCATION_ERROR;
2518 work->setLenient(TRUE);
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;
2526 work->set(field, fieldValue);
2527 if (work->get(field, status) != fieldValue) {
2531 result = fieldValue;
2534 } while (fieldValue >= endValue);
2538 /* Test for buffer overflows */
2539 if(U_FAILURE(status)) {
2545 // -------------------------------------
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.
2554 * @see #validateField(int)
2556 void 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);
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)
2571 void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) {
2574 case UCAL_DAY_OF_MONTH:
2575 y = handleGetExtendedYear();
2576 validateField(field, 1, handleGetMonthLength(y, internalGet(UCAL_MONTH)), status);
2578 case UCAL_DAY_OF_YEAR:
2579 y = handleGetExtendedYear();
2580 validateField(field, 1, handleGetYearLength(y), status);
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__);
2588 status = U_ILLEGAL_ARGUMENT_ERROR; // "DAY_OF_WEEK_IN_MONTH cannot be zero"
2591 validateField(field, getMinimum(field), getMaximum(field), status);
2594 validateField(field, getMinimum(field), getMaximum(field), status);
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)}.
2606 void Calendar::validateField(UCalendarDateFields field, int32_t min, int32_t max, UErrorCode& status)
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);
2614 status = U_ILLEGAL_ARGUMENT_ERROR;
2619 // -------------------------
2621 const UFieldResolutionTable* Calendar::getFieldResolutionTable() const {
2622 return kDatePrecedence;
2626 UCalendarDateFields Calendar::newerField(UCalendarDateFields defaultField, UCalendarDateFields alternateField) const
2628 if (fStamp[alternateField] > fStamp[defaultField]) {
2629 return alternateField;
2631 return defaultField;
2634 UCalendarDateFields 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
2648 } else if(s > lineStamp) {
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;
2662 bestField = tempBestField;
2665 if (bestField == tempBestField) {
2666 bestStamp = lineStamp;
2673 return (UCalendarDateFields)bestField;
2676 const UFieldResolutionTable Calendar::kDatePrecedence[] =
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
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 },
2703 const UFieldResolutionTable Calendar::kDOWPrecedence[] =
2706 { UCAL_DAY_OF_WEEK,kResolveSTOP, kResolveSTOP },
2707 { UCAL_DOW_LOCAL,kResolveSTOP, kResolveSTOP },
2713 // precedence for calculating a year
2714 const UFieldResolutionTable Calendar::kYearPrecedence[] =
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
2726 // -------------------------
2729 void Calendar::computeTime(UErrorCode& status) {
2731 validateFields(status);
2732 if (U_FAILURE(status)) {
2737 // Compute the Julian day
2738 int32_t julianDay = computeJulianDay();
2740 double millis = Grego::julianDayToMillis(julianDay);
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);
2751 int32_t millisInDay;
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);
2762 millisInDay = computeMillisInDay();
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));
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.
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
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.
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.
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.
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;
2808 fZone->getOffset(tmpTime, FALSE, raw, dst, status);
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)) {
2815 status = U_ILLEGAL_ARGUMENT_ERROR;
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.
2822 BasicTimeZone *btz = getBasicTimeZone();
2824 TimeZoneTransition transition;
2825 UBool hasTransition = btz->getPreviousTransition(tmpTime, TRUE, transition);
2826 if (hasTransition) {
2827 t = transition.getTime();
2829 // Could not find any transitions.
2830 // Note: This should never happen.
2831 status = U_INTERNAL_PROGRAM_ERROR;
2834 // If not BasicTimeZone, return unsupported error for now.
2835 // TODO: We may support non-BasicTimeZone in future.
2836 status = U_UNSUPPORTED_ERROR;
2844 t = millis + millisInDay - computeZoneOffset(millis, millisInDay, status);
2847 if (U_SUCCESS(status)) {
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.
2859 int32_t Calendar::computeMillisInDay() {
2860 // Do the time portion of the conversion.
2862 int32_t millisInDay = 0;
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;
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);
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
2885 // We use the fact that unset == 0; we start with millisInDay
2888 millisInDay += internalGet(UCAL_MINUTE); // now have minutes
2890 millisInDay += internalGet(UCAL_SECOND); // now have seconds
2891 millisInDay *= 1000;
2892 millisInDay += internalGet(UCAL_MILLISECOND); // now have millis
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
2904 int32_t Calendar::computeZoneOffset(double millis, int32_t millisInDay, UErrorCode &ec) {
2905 int32_t rawOffset, dstOffset;
2906 UDate wall = millis + millisInDay;
2907 BasicTimeZone* btz = getBasicTimeZone();
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);
2913 const TimeZone& tz = getTimeZone();
2914 // By default, TimeZone::getOffset behaves UCAL_WALLTIME_LAST for both.
2915 tz.getOffset(wall, TRUE, rawOffset, dstOffset, ec);
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);
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);
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);
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);
2948 return rawOffset + dstOffset;
2951 int32_t Calendar::computeJulianDay()
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
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);
2969 UCalendarDateFields bestField = resolveFields(getFieldResolutionTable());
2970 if (bestField == UCAL_FIELD_COUNT) {
2971 bestField = UCAL_DAY_OF_MONTH;
2974 return handleComputeJulianDay(bestField);
2977 // -------------------------------------------
2979 int32_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);
2985 if (bestField == UCAL_WEEK_OF_YEAR) {
2986 year = internalGet(UCAL_YEAR_WOY, handleGetExtendedYear());
2987 internalSet(UCAL_EXTENDED_YEAR, year);
2989 year = handleGetExtendedYear();
2990 internalSet(UCAL_EXTENDED_YEAR, year);
2993 #if defined (U_DEBUG_CAL)
2994 fprintf(stderr, "%s:%d: bestField= %s - y=%d\n", __FILE__, __LINE__, fldName(bestField), year);
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.
3000 // give calendar subclass a chance to have a default 'first' month
3003 if(isSet(UCAL_MONTH)) {
3004 month = internalGet(UCAL_MONTH);
3006 month = getDefaultMonthInYear(year);
3009 int32_t julianDay = handleComputeMonthStart(year, useMonth ? month : 0, useMonth);
3011 if (bestField == UCAL_DAY_OF_MONTH) {
3013 // give calendar subclass a chance to have a default 'first' dom
3015 if(isSet(UCAL_DAY_OF_MONTH)) {
3016 dayOfMonth = internalGet(UCAL_DAY_OF_MONTH,1);
3018 dayOfMonth = getDefaultDayInMonth(year, month);
3020 return julianDay + dayOfMonth;
3023 if (bestField == UCAL_DAY_OF_YEAR) {
3024 return julianDay + internalGet(UCAL_DAY_OF_YEAR);
3027 int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw
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)
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.
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;
3046 int32_t dowLocal = getLocalDOW();
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;
3053 if (bestField == UCAL_DAY_OF_WEEK_IN_MONTH) {
3054 // Adjust the target DOW to be in the month or year.
3059 // The only trickiness occurs if the day-of-week-in-month is
3061 int32_t dim = internalGet(UCAL_DAY_OF_WEEK_IN_MONTH, 1);
3063 date += 7*(dim - 1);
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;
3077 #if defined (U_DEBUG_CAL)
3078 fprintf(stderr, "%s:%d - bf= %s\n", __FILE__, __LINE__, fldName(bestField));
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)
3086 // need to be sure to stay in 'real' year.
3087 int32_t woy = internalGet(bestField);
3089 int32_t nextJulianDay = handleComputeMonthStart(year+1, 0, FALSE); // jd of day before jan 1
3090 int32_t nextFirst = julianDayToDayOfWeek(nextJulianDay + 1) - firstDayOfWeek;
3092 if (nextFirst < 0) { // 0..6 ldow of Jan 1
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);
3102 fprintf(stderr, " next: %d DFW, min=%d \n", (7-nextFirst), getMinimalDaysInFirstWeek() );
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
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));
3114 julianDay = nextJulianDay;
3116 // recalculate 'first' [0-based local dow of jan 1]
3117 first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek;
3121 // recalculate date.
3122 date = 1 - first + dowLocal;
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()) {
3131 // Now adjust for the week number.
3132 testDate += 7 * (woy - 1);
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);
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
3143 if(first < 0) { // 0..6
3146 date = 1 - first + dowLocal;
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);
3154 } /* correction needed */
3155 } /* leastmaximum */
3156 } /* resolvefields(year) != year_woy */
3157 } /* bestfield != week_of_year */
3159 // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR)
3160 // Adjust for minimal days in first week
3161 if ((7 - first) < getMinimalDaysInFirstWeek()) {
3165 // Now adjust for the week number.
3166 date += 7 * (internalGet(bestField) - 1);
3169 return julianDay + date;
3173 Calendar::getDefaultMonthInYear(int32_t /*eyear*/)
3179 Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/)
3185 int32_t Calendar::getLocalDOW()
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;
3194 case UCAL_DOW_LOCAL:
3195 dowLocal = internalGet(UCAL_DOW_LOCAL) - 1;
3200 dowLocal = dowLocal % 7;
3207 int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy)
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)
3214 // First, we need a reliable DOW.
3215 UCalendarDateFields bestField = resolveFields(kDatePrecedence); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields
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
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)
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.
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;
3239 int32_t nextFirst = julianDayToDayOfWeek(nextJan1Start + 1) - firstDayOfWeek;
3240 if (nextFirst < 0) {
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?
3248 if((7 - first) < minDays) {
3249 jan1InPrevYear = TRUE;
3252 // if((7 - nextFirst) < minDays) {
3253 // nextJan1InPrevYear = TRUE;
3257 case UCAL_WEEK_OF_YEAR:
3259 if(jan1InPrevYear == TRUE) {
3260 // the first week of January is in the previous year
3261 // therefore WOY1 is always solidly within yearWoy
3264 // First WOY is split between two years
3265 if( dowLocal < first) { // we are prior to Jan 1
3266 return yearWoy-1; // previous year
3268 return yearWoy; // in this year
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.
3282 if( (jd+1) >= nextJan1Start ) {
3283 // we are in week 52 or 53 etc. - actual year is yearWoy+1
3286 // still in yearWoy;
3290 // we're not possibly in the last week -must be ywoy
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
3299 //if(nextJan1InPrevYear) {
3300 if(internalGet(UCAL_MONTH)==0) {
3308 //(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow */ ) {
3309 //within 1st week and in this month..
3313 default: // assume the year is appropriate
3318 int32_t Calendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const
3320 return handleComputeMonthStart(extendedYear, month+1, TRUE) -
3321 handleComputeMonthStart(extendedYear, month, TRUE);
3324 int32_t Calendar::handleGetYearLength(int32_t eyear) const {
3325 return handleComputeMonthStart(eyear+1, 0, FALSE) -
3326 handleComputeMonthStart(eyear, 0, FALSE);
3330 Calendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const
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));
3346 case UCAL_DAY_OF_YEAR:
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));
3358 case UCAL_DAY_OF_WEEK:
3361 case UCAL_HOUR_OF_DAY:
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);
3375 // For all other fields, do it the hard way....
3376 result = getActualHelper(field, getLeastMaximum(field), getMaximum(field),status);
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.
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.
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.
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.
3404 void Calendar::prepareGetActual(UCalendarDateFields field, UBool isMinimum, UErrorCode &status)
3406 set(UCAL_MILLISECONDS_IN_DAY, 0);
3410 case UCAL_EXTENDED_YEAR:
3411 set(UCAL_DAY_OF_YEAR, getGreatestMinimum(UCAL_DAY_OF_YEAR));
3415 set(UCAL_WEEK_OF_YEAR, getGreatestMinimum(UCAL_WEEK_OF_YEAR));
3418 set(UCAL_DATE, getGreatestMinimum(UCAL_DATE));
3421 case UCAL_DAY_OF_WEEK_IN_MONTH:
3422 // For dowim, the maximum occurs for the DOW of the first of the
3425 set(UCAL_DAY_OF_WEEK, get(UCAL_DAY_OF_WEEK, status)); // Make this user set
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.
3435 int32_t dow = fFirstDayOfWeek;
3437 dow = (dow + 6) % 7; // set to last DOW
3438 if (dow < UCAL_SUNDAY) {
3442 #if defined (U_DEBUG_CAL)
3443 fprintf(stderr, "prepareGetActualHelper(WOM/WOY) - dow=%d\n", dow);
3445 set(UCAL_DAY_OF_WEEK, dow);
3452 // Do this last to give it the newest time stamp
3453 set(field, getGreatestMinimum(field));
3456 int32_t Calendar::getActualHelper(UCalendarDateFields field, int32_t startValue, int32_t endValue, UErrorCode &status) const
3458 #if defined (U_DEBUG_CAL)
3459 fprintf(stderr, "getActualHelper(%d,%d .. %d, %s)\n", field, startValue, endValue, u_errorName(status));
3461 if (startValue == endValue) {
3462 // if we know that the maximum value is always the same, just return it
3466 int32_t delta = (endValue > startValue) ? 1 : -1;
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; }
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);
3478 work->setLenient(TRUE);
3479 work->prepareGetActual(field, delta < 0, status);
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);
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));
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));
3507 result = startValue;
3508 } while (startValue != endValue);
3511 #if defined (U_DEBUG_CAL)
3512 fprintf(stderr, "getActualHelper(%d) = %d\n", field, result);
3520 // -------------------------------------
3523 Calendar::setWeekData(const Locale& desiredLocale, const char *type, UErrorCode& status)
3526 if (U_FAILURE(status)) return;
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
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" )
3543 char minLocaleID[ULOC_FULLNAME_CAPACITY] = { 0 };
3544 UErrorCode myStatus = U_ZERO_ERROR;
3546 uloc_minimizeSubtags(desiredLocale.getName(),minLocaleID,ULOC_FULLNAME_CAPACITY,&myStatus);
3547 Locale min = Locale::createFromName(minLocaleID);
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());
3557 useLocale = Locale(desiredLocale);
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) */
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));
3573 status = U_USING_FALLBACK_WARNING;
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);
3587 if (U_FAILURE(status)) {
3588 #if defined (U_DEBUG_CALDATA)
3589 fprintf(stderr, " Failure loading weekData from supplemental = %s\n", u_errorName(status));
3591 status = U_USING_FALLBACK_WARNING;
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];
3607 status = U_INVALID_FORMAT_ERROR;
3610 ures_close(weekData);
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.
3620 Calendar::updateTime(UErrorCode& status)
3622 computeTime(status);
3623 if(U_FAILURE(status))
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;
3633 fAreFieldsVirtuallySet = FALSE;
3637 Calendar::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
3638 U_LOCALE_BASED(locBased, *this);
3639 return locBased.getLocale(type, status);
3643 Calendar::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const {
3644 U_LOCALE_BASED(locBased, *this);
3645 return locBased.getLocaleID(type, status);
3649 Calendar::recalculateStamp() {
3651 int32_t currentValue;
3656 for (j = 0; j < UCAL_FIELD_COUNT; j++) {
3657 currentValue = STAMP_MAX;
3659 for (i = 0; i < UCAL_FIELD_COUNT; i++) {
3660 if (fStamp[i] > fNextStamp && fStamp[i] < currentValue) {
3661 currentValue = fStamp[i];
3667 fStamp[index] = ++fNextStamp;
3675 // Deprecated function. This doesn't need to be inline.
3677 Calendar::internalSet(EDateFields field, int32_t value)
3679 internalSet((UCalendarDateFields) field, value);
3683 Calendar::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;
3695 #endif /* #if !UCONFIG_NO_FORMATTING */