2 *******************************************************************************
3 * Copyright (C) 1997-2016, 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"
60 #include "sharedcalendar.h"
61 #include "unifiedcache.h"
64 #if !UCONFIG_NO_SERVICE
65 static icu::ICULocaleService
* gService
= NULL
;
66 static icu::UInitOnce gServiceInitOnce
= U_INITONCE_INITIALIZER
;
69 // INTERNAL - for cleanup
72 static UBool
calendar_cleanup(void) {
73 #if !UCONFIG_NO_SERVICE
78 gServiceInitOnce
.reset();
84 // ------------------------------------------
88 //-------------------------------------------
89 //#define U_DEBUG_CALSVC 1
92 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
95 * fldName was removed as a duplicate implementation.
96 * use udbg_ services instead,
97 * which depend on include files and library from ../tools/toolutil, the following circular link:
98 * CPPFLAGS+=-I$(top_srcdir)/tools/toolutil
99 * LIBS+=$(LIBICUTOOLUTIL)
101 #include "udbgutil.h"
105 * convert a UCalendarDateFields into a string - for debugging
106 * @param f field enum
107 * @return static string to the field name
111 const char* fldName(UCalendarDateFields f
) {
112 return udbg_enumName(UDBG_UCalendarDateFields
, (int32_t)f
);
116 // from CalendarTest::calToStr - but doesn't modify contents.
117 void ucal_dump(const Calendar
&cal
) {
121 void Calendar::dump() const {
123 fprintf(stderr
, "@calendar=%s, timeset=%c, fieldset=%c, allfields=%c, virtualset=%c, t=%.2f",
124 getType(), fIsTimeSet
?'y':'n', fAreFieldsSet
?'y':'n', fAreAllFieldsSet
?'y':'n',
125 fAreFieldsVirtuallySet
?'y':'n',
128 // can add more things here: DST, zone, etc.
129 fprintf(stderr
, "\n");
130 for(i
= 0;i
<UCAL_FIELD_COUNT
;i
++) {
132 const char *f
= fldName((UCalendarDateFields
)i
);
133 fprintf(stderr
, " %25s: %-11ld", f
, fFields
[i
]);
134 if(fStamp
[i
] == kUnset
) {
135 fprintf(stderr
, " (unset) ");
136 } else if(fStamp
[i
] == kInternallySet
) {
137 fprintf(stderr
, " (internally set) ");
138 //} else if(fStamp[i] == kInternalDefault) {
139 // fprintf(stderr, " (internal default) ");
141 fprintf(stderr
, " %%%d ", fStamp
[i
]);
143 fprintf(stderr
, "\n");
148 U_CFUNC
void ucal_dump(UCalendar
* cal
) {
149 ucal_dump( *((Calendar
*)cal
) );
155 /* Max value for stamp allowable before recalculation */
156 #define STAMP_MAX 10000
158 static const char * const gCalTypes
[] = {
171 "ethiopic-amete-alem",
180 // Must be in the order of gCalTypes above
181 typedef enum ECalType
{
182 CALTYPE_UNKNOWN
= -1,
183 CALTYPE_GREGORIAN
= 0,
188 CALTYPE_ISLAMIC_CIVIL
,
195 CALTYPE_ETHIOPIC_AMETE_ALEM
,
198 CALTYPE_ISLAMIC_UMALQURA
,
199 CALTYPE_ISLAMIC_TBLA
,
205 SharedCalendar::~SharedCalendar() {
209 template<> U_I18N_API
210 const SharedCalendar
*LocaleCacheKey
<SharedCalendar
>::createObject(
211 const void * /*unusedCreationContext*/, UErrorCode
&status
) const {
212 Calendar
*calendar
= Calendar::makeInstance(fLoc
, status
);
213 if (U_FAILURE(status
)) {
216 SharedCalendar
*shared
= new SharedCalendar(calendar
);
217 if (shared
== NULL
) {
219 status
= U_MEMORY_ALLOCATION_ERROR
;
226 static ECalType
getCalendarType(const char *s
) {
227 for (int i
= 0; gCalTypes
[i
] != NULL
; i
++) {
228 if (uprv_stricmp(s
, gCalTypes
[i
]) == 0) {
232 return CALTYPE_UNKNOWN
;
235 static UBool
isStandardSupportedKeyword(const char *keyword
, UErrorCode
& status
) {
236 if(U_FAILURE(status
)) {
239 ECalType calType
= getCalendarType(keyword
);
240 return (calType
!= CALTYPE_UNKNOWN
);
243 static void getCalendarKeyword(const UnicodeString
&id
, char *targetBuffer
, int32_t targetBufferSize
) {
244 UnicodeString calendarKeyword
= UNICODE_STRING_SIMPLE("calendar=");
245 int32_t calKeyLen
= calendarKeyword
.length();
248 int32_t keywordIdx
= id
.indexOf((UChar
)0x003D); /* '=' */
249 if (id
[0] == 0x40/*'@'*/
250 && id
.compareBetween(1, keywordIdx
+1, calendarKeyword
, 0, calKeyLen
) == 0)
252 keyLen
= id
.extract(keywordIdx
+1, id
.length(), targetBuffer
, targetBufferSize
, US_INV
);
254 targetBuffer
[keyLen
] = 0;
257 static ECalType
getCalendarTypeForLocale(const char *locid
) {
258 UErrorCode status
= U_ZERO_ERROR
;
259 ECalType calType
= CALTYPE_UNKNOWN
;
261 //TODO: ULOC_FULL_NAME is out of date and too small..
262 char canonicalName
[256];
264 // canonicalize, so grandfathered variant will be transformed to keywords
265 // e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese
266 int32_t canonicalLen
= uloc_canonicalize(locid
, canonicalName
, sizeof(canonicalName
) - 1, &status
);
267 if (U_FAILURE(status
)) {
268 return CALTYPE_GREGORIAN
;
270 canonicalName
[canonicalLen
] = 0; // terminate
273 int32_t calTypeBufLen
;
275 calTypeBufLen
= uloc_getKeywordValue(canonicalName
, "calendar", calTypeBuf
, sizeof(calTypeBuf
) - 1, &status
);
276 if (U_SUCCESS(status
)) {
277 calTypeBuf
[calTypeBufLen
] = 0;
278 calType
= getCalendarType(calTypeBuf
);
279 if (calType
!= CALTYPE_UNKNOWN
) {
283 status
= U_ZERO_ERROR
;
285 // when calendar keyword is not available or not supported, read supplementalData
286 // to get the default calendar type for the locale's region
287 char region
[ULOC_COUNTRY_CAPACITY
];
288 (void)ulocimp_getRegionForSupplementalData(canonicalName
, TRUE
, region
, sizeof(region
), &status
);
289 if (U_FAILURE(status
)) {
290 return CALTYPE_GREGORIAN
;
293 // Read preferred calendar values from supplementalData calendarPreference
294 UResourceBundle
*rb
= ures_openDirect(NULL
, "supplementalData", &status
);
295 ures_getByKey(rb
, "calendarPreferenceData", rb
, &status
);
296 UResourceBundle
*order
= ures_getByKey(rb
, region
, NULL
, &status
);
297 if (status
== U_MISSING_RESOURCE_ERROR
&& rb
!= NULL
) {
298 status
= U_ZERO_ERROR
;
299 order
= ures_getByKey(rb
, "001", NULL
, &status
);
303 if (U_SUCCESS(status
) && order
!= NULL
) {
304 // the first calender type is the default for the region
306 const UChar
*uCalType
= ures_getStringByIndex(order
, 0, &len
, &status
);
307 if (len
< (int32_t)sizeof(calTypeBuf
)) {
308 u_UCharsToChars(uCalType
, calTypeBuf
, len
);
309 *(calTypeBuf
+ len
) = 0; // terminate;
310 calType
= getCalendarType(calTypeBuf
);
317 if (calType
== CALTYPE_UNKNOWN
) {
319 calType
= CALTYPE_GREGORIAN
;
324 static Calendar
*createStandardCalendar(ECalType calType
, const Locale
&loc
, UErrorCode
& status
) {
325 Calendar
*cal
= NULL
;
328 case CALTYPE_GREGORIAN
:
329 cal
= new GregorianCalendar(loc
, status
);
331 case CALTYPE_JAPANESE
:
332 cal
= new JapaneseCalendar(loc
, status
);
334 case CALTYPE_BUDDHIST
:
335 cal
= new BuddhistCalendar(loc
, status
);
338 cal
= new TaiwanCalendar(loc
, status
);
340 case CALTYPE_PERSIAN
:
341 cal
= new PersianCalendar(loc
, status
);
343 case CALTYPE_ISLAMIC_TBLA
:
344 cal
= new IslamicCalendar(loc
, status
, IslamicCalendar::TBLA
);
346 case CALTYPE_ISLAMIC_CIVIL
:
347 cal
= new IslamicCalendar(loc
, status
, IslamicCalendar::CIVIL
);
349 case CALTYPE_ISLAMIC_RGSA
:
350 // default any region specific not handled individually to islamic
351 case CALTYPE_ISLAMIC
:
352 cal
= new IslamicCalendar(loc
, status
, IslamicCalendar::ASTRONOMICAL
);
354 case CALTYPE_ISLAMIC_UMALQURA
:
355 cal
= new IslamicCalendar(loc
, status
, IslamicCalendar::UMALQURA
);
358 cal
= new HebrewCalendar(loc
, status
);
360 case CALTYPE_CHINESE
:
361 cal
= new ChineseCalendar(loc
, status
);
364 cal
= new IndianCalendar(loc
, status
);
367 cal
= new CopticCalendar(loc
, status
);
369 case CALTYPE_ETHIOPIC
:
370 cal
= new EthiopicCalendar(loc
, status
, EthiopicCalendar::AMETE_MIHRET_ERA
);
372 case CALTYPE_ETHIOPIC_AMETE_ALEM
:
373 cal
= new EthiopicCalendar(loc
, status
, EthiopicCalendar::AMETE_ALEM_ERA
);
375 case CALTYPE_ISO8601
:
376 cal
= new GregorianCalendar(loc
, status
);
377 cal
->setFirstDayOfWeek(UCAL_MONDAY
);
378 cal
->setMinimalDaysInFirstWeek(4);
381 cal
= new DangiCalendar(loc
, status
);
384 status
= U_UNSUPPORTED_ERROR
;
390 #if !UCONFIG_NO_SERVICE
392 // -------------------------------------
395 * a Calendar Factory which creates the "basic" calendar types, that is, those
398 class BasicCalendarFactory
: public LocaleKeyFactory
{
401 * @param calendarType static const string (caller owns storage - will be aliased) to calendar type
403 BasicCalendarFactory()
404 : LocaleKeyFactory(LocaleKeyFactory::INVISIBLE
) { }
406 virtual ~BasicCalendarFactory();
409 //virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const {
410 // if(U_FAILURE(status)) {
413 // char keyword[ULOC_FULLNAME_CAPACITY];
414 // getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword));
415 // return isStandardSupportedKeyword(keyword, status);
418 virtual void updateVisibleIDs(Hashtable
& result
, UErrorCode
& status
) const
420 if (U_SUCCESS(status
)) {
421 for(int32_t i
=0;gCalTypes
[i
] != NULL
;i
++) {
422 UnicodeString
id((UChar
)0x40); /* '@' a variant character */
423 id
.append(UNICODE_STRING_SIMPLE("calendar="));
424 id
.append(UnicodeString(gCalTypes
[i
], -1, US_INV
));
425 result
.put(id
, (void*)this, status
);
430 virtual UObject
* create(const ICUServiceKey
& key
, const ICUService
* /*service*/, UErrorCode
& status
) const {
431 #ifdef U_DEBUG_CALSVC
432 if(dynamic_cast<const LocaleKey
*>(&key
) == NULL
) {
433 fprintf(stderr
, "::create - not a LocaleKey!\n");
436 const LocaleKey
& lkey
= (LocaleKey
&)key
;
437 Locale curLoc
; // current locale
438 Locale canLoc
; // Canonical locale
440 lkey
.currentLocale(curLoc
);
441 lkey
.canonicalLocale(canLoc
);
443 char keyword
[ULOC_FULLNAME_CAPACITY
];
447 getCalendarKeyword(str
, keyword
, (int32_t) sizeof(keyword
));
449 #ifdef U_DEBUG_CALSVC
450 fprintf(stderr
, "BasicCalendarFactory::create() - cur %s, can %s\n", (const char*)curLoc
.getName(), (const char*)canLoc
.getName());
453 if(!isStandardSupportedKeyword(keyword
,status
)) { // Do we handle this type?
454 #ifdef U_DEBUG_CALSVC
456 fprintf(stderr
, "BasicCalendarFactory - not handling %s.[%s]\n", (const char*) curLoc
.getName(), tmp
);
461 return createStandardCalendar(getCalendarType(keyword
), canLoc
, status
);
465 BasicCalendarFactory::~BasicCalendarFactory() {}
468 * A factory which looks up the DefaultCalendar resource to determine which class of calendar to use
471 class DefaultCalendarFactory
: public ICUResourceBundleFactory
{
473 DefaultCalendarFactory() : ICUResourceBundleFactory() { }
474 virtual ~DefaultCalendarFactory();
476 virtual UObject
* create(const ICUServiceKey
& key
, const ICUService
* /*service*/, UErrorCode
& status
) const {
478 LocaleKey
&lkey
= (LocaleKey
&)key
;
480 lkey
.currentLocale(loc
);
482 UnicodeString
*ret
= new UnicodeString();
484 status
= U_MEMORY_ALLOCATION_ERROR
;
486 ret
->append((UChar
)0x40); // '@' is a variant character
487 ret
->append(UNICODE_STRING("calendar=", 9));
488 ret
->append(UnicodeString(gCalTypes
[getCalendarTypeForLocale(loc
.getName())], -1, US_INV
));
494 DefaultCalendarFactory::~DefaultCalendarFactory() {}
496 // -------------------------------------
497 class CalendarService
: public ICULocaleService
{
500 : ICULocaleService(UNICODE_STRING_SIMPLE("Calendar"))
502 UErrorCode status
= U_ZERO_ERROR
;
503 registerFactory(new DefaultCalendarFactory(), status
);
506 virtual ~CalendarService();
508 virtual UObject
* cloneInstance(UObject
* instance
) const {
509 UnicodeString
*s
= dynamic_cast<UnicodeString
*>(instance
);
513 #ifdef U_DEBUG_CALSVC_F
514 UErrorCode status2
= U_ZERO_ERROR
;
515 fprintf(stderr
, "Cloning a %s calendar with tz=%ld\n", ((Calendar
*)instance
)->getType(), ((Calendar
*)instance
)->get(UCAL_ZONE_OFFSET
, status2
));
517 return ((Calendar
*)instance
)->clone();
521 virtual UObject
* handleDefault(const ICUServiceKey
& key
, UnicodeString
* /*actualID*/, UErrorCode
& status
) const {
522 LocaleKey
& lkey
= (LocaleKey
&)key
;
523 //int32_t kind = lkey.kind();
526 lkey
.canonicalLocale(loc
);
528 #ifdef U_DEBUG_CALSVC
530 lkey
.currentLocale(loc2
);
531 fprintf(stderr
, "CalSvc:handleDefault for currentLoc %s, canloc %s\n", (const char*)loc
.getName(), (const char*)loc2
.getName());
533 Calendar
*nc
= new GregorianCalendar(loc
, status
);
535 #ifdef U_DEBUG_CALSVC
536 UErrorCode status2
= U_ZERO_ERROR
;
537 fprintf(stderr
, "New default calendar has tz=%d\n", ((Calendar
*)nc
)->get(UCAL_ZONE_OFFSET
, status2
));
542 virtual UBool
isDefault() const {
543 return countFactories() == 1;
547 CalendarService::~CalendarService() {}
549 // -------------------------------------
552 isCalendarServiceUsed() {
553 return !gServiceInitOnce
.isReset();
556 // -------------------------------------
558 static void U_CALLCONV
559 initCalendarService(UErrorCode
&status
)
561 #ifdef U_DEBUG_CALSVC
562 fprintf(stderr
, "Spinning up Calendar Service\n");
564 ucln_i18n_registerCleanup(UCLN_I18N_CALENDAR
, calendar_cleanup
);
565 gService
= new CalendarService();
566 if (gService
== NULL
) {
567 status
= U_MEMORY_ALLOCATION_ERROR
;
570 #ifdef U_DEBUG_CALSVC
571 fprintf(stderr
, "Registering classes..\n");
574 // Register all basic instances.
575 gService
->registerFactory(new BasicCalendarFactory(),status
);
577 #ifdef U_DEBUG_CALSVC
578 fprintf(stderr
, "Done..\n");
581 if(U_FAILURE(status
)) {
582 #ifdef U_DEBUG_CALSVC
583 fprintf(stderr
, "err (%s) registering classes, deleting service.....\n", u_errorName(status
));
590 static ICULocaleService
*
591 getCalendarService(UErrorCode
&status
)
593 umtx_initOnce(gServiceInitOnce
, &initCalendarService
, status
);
597 URegistryKey
Calendar::registerFactory(ICUServiceFactory
* toAdopt
, UErrorCode
& status
)
599 return getCalendarService(status
)->registerFactory(toAdopt
, status
);
602 UBool
Calendar::unregister(URegistryKey key
, UErrorCode
& status
) {
603 return getCalendarService(status
)->unregister(key
, status
);
605 #endif /* UCONFIG_NO_SERVICE */
607 // -------------------------------------
609 static const int32_t kCalendarLimits
[UCAL_FIELD_COUNT
][4] = {
610 // Minimum Greatest min Least max Greatest max
611 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // ERA
612 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR
613 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // MONTH
614 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_YEAR
615 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_MONTH
616 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_MONTH
617 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_YEAR
618 { 1, 1, 7, 7 }, // DAY_OF_WEEK
619 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_WEEK_IN_MONTH
620 { 0, 0, 1, 1 }, // AM_PM
621 { 0, 0, 11, 11 }, // HOUR
622 { 0, 0, 23, 23 }, // HOUR_OF_DAY
623 { 0, 0, 59, 59 }, // MINUTE
624 { 0, 0, 59, 59 }, // SECOND
625 { 0, 0, 999, 999 }, // MILLISECOND
626 {-12*kOneHour
, -12*kOneHour
, 12*kOneHour
, 15*kOneHour
}, // ZONE_OFFSET
627 { 0, 0, 1*kOneHour
, 1*kOneHour
}, // DST_OFFSET
628 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR_WOY
629 { 1, 1, 7, 7 }, // DOW_LOCAL
630 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // EXTENDED_YEAR
631 { -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY
632 { 0, 0, 24*kOneHour
-1, 24*kOneHour
-1 }, // MILLISECONDS_IN_DAY
633 { 0, 0, 1, 1 }, // IS_LEAP_MONTH
636 // Resource bundle tags read by this class
637 static const char gMonthNames
[] = "monthNames";
639 // Data flow in Calendar
640 // ---------------------
642 // The current time is represented in two ways by Calendar: as UTC
643 // milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local
644 // fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the
645 // millis from the fields, and vice versa. The data needed to do this
646 // conversion is encapsulated by a TimeZone object owned by the Calendar.
647 // The data provided by the TimeZone object may also be overridden if the
648 // user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class
649 // keeps track of what information was most recently set by the caller, and
650 // uses that to compute any other information as needed.
652 // If the user sets the fields using set(), the data flow is as follows.
653 // This is implemented by the Calendar subclass's computeTime() method.
654 // During this process, certain fields may be ignored. The disambiguation
655 // algorithm for resolving which fields to pay attention to is described
658 // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
660 // | Using Calendar-specific algorithm
662 // local standard millis
664 // | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET
666 // UTC millis (in time data member)
668 // If the user sets the UTC millis using setTime(), the data flow is as
669 // follows. This is implemented by the Calendar subclass's computeFields()
672 // UTC millis (in time data member)
674 // | Using TimeZone getOffset()
676 // local standard millis
678 // | Using Calendar-specific algorithm
680 // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
682 // In general, a round trip from fields, through local and UTC millis, and
683 // back out to fields is made when necessary. This is implemented by the
684 // complete() method. Resolving a partial set of fields into a UTC millis
685 // value allows all remaining fields to be generated from that value. If
686 // the Calendar is lenient, the fields are also renormalized to standard
687 // ranges when they are regenerated.
689 // -------------------------------------
691 Calendar::Calendar(UErrorCode
& success
)
694 fAreFieldsSet(FALSE
),
695 fAreAllFieldsSet(FALSE
),
696 fAreFieldsVirtuallySet(FALSE
),
697 fNextStamp((int32_t)kMinimumUserStamp
),
701 fRepeatedWallTime(UCAL_WALLTIME_LAST
),
702 fSkippedWallTime(UCAL_WALLTIME_LAST
)
705 if (U_FAILURE(success
)) {
708 fZone
= TimeZone::createDefault();
710 success
= U_MEMORY_ALLOCATION_ERROR
;
712 setWeekData(Locale::getDefault(), NULL
, success
);
715 // -------------------------------------
717 Calendar::Calendar(TimeZone
* zone
, const Locale
& aLocale
, UErrorCode
& success
)
720 fAreFieldsSet(FALSE
),
721 fAreAllFieldsSet(FALSE
),
722 fAreFieldsVirtuallySet(FALSE
),
723 fNextStamp((int32_t)kMinimumUserStamp
),
727 fRepeatedWallTime(UCAL_WALLTIME_LAST
),
728 fSkippedWallTime(UCAL_WALLTIME_LAST
)
730 if (U_FAILURE(success
)) {
734 #if defined (U_DEBUG_CAL)
735 fprintf(stderr
, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n",
738 success
= U_ILLEGAL_ARGUMENT_ERROR
;
744 setWeekData(aLocale
, NULL
, success
);
747 // -------------------------------------
749 Calendar::Calendar(const TimeZone
& zone
, const Locale
& aLocale
, UErrorCode
& success
)
752 fAreFieldsSet(FALSE
),
753 fAreAllFieldsSet(FALSE
),
754 fAreFieldsVirtuallySet(FALSE
),
755 fNextStamp((int32_t)kMinimumUserStamp
),
759 fRepeatedWallTime(UCAL_WALLTIME_LAST
),
760 fSkippedWallTime(UCAL_WALLTIME_LAST
)
762 if (U_FAILURE(success
)) {
766 fZone
= zone
.clone();
768 success
= U_MEMORY_ALLOCATION_ERROR
;
770 setWeekData(aLocale
, NULL
, success
);
773 // -------------------------------------
775 Calendar::~Calendar()
780 // -------------------------------------
782 Calendar::Calendar(const Calendar
&source
)
789 // -------------------------------------
792 Calendar::operator=(const Calendar
&right
)
794 if (this != &right
) {
795 uprv_arrayCopy(right
.fFields
, fFields
, UCAL_FIELD_COUNT
);
796 uprv_arrayCopy(right
.fIsSet
, fIsSet
, UCAL_FIELD_COUNT
);
797 uprv_arrayCopy(right
.fStamp
, fStamp
, UCAL_FIELD_COUNT
);
799 fIsTimeSet
= right
.fIsTimeSet
;
800 fAreAllFieldsSet
= right
.fAreAllFieldsSet
;
801 fAreFieldsSet
= right
.fAreFieldsSet
;
802 fAreFieldsVirtuallySet
= right
.fAreFieldsVirtuallySet
;
803 fLenient
= right
.fLenient
;
804 fRepeatedWallTime
= right
.fRepeatedWallTime
;
805 fSkippedWallTime
= right
.fSkippedWallTime
;
808 if (right
.fZone
!= NULL
) {
809 fZone
= right
.fZone
->clone();
811 fFirstDayOfWeek
= right
.fFirstDayOfWeek
;
812 fMinimalDaysInFirstWeek
= right
.fMinimalDaysInFirstWeek
;
813 fWeekendOnset
= right
.fWeekendOnset
;
814 fWeekendOnsetMillis
= right
.fWeekendOnsetMillis
;
815 fWeekendCease
= right
.fWeekendCease
;
816 fWeekendCeaseMillis
= right
.fWeekendCeaseMillis
;
817 fNextStamp
= right
.fNextStamp
;
818 uprv_strcpy(validLocale
, right
.validLocale
);
819 uprv_strcpy(actualLocale
, right
.actualLocale
);
825 // -------------------------------------
828 Calendar::createInstance(UErrorCode
& success
)
830 return createInstance(TimeZone::createDefault(), Locale::getDefault(), success
);
833 // -------------------------------------
836 Calendar::createInstance(const TimeZone
& zone
, UErrorCode
& success
)
838 return createInstance(zone
, Locale::getDefault(), success
);
841 // -------------------------------------
844 Calendar::createInstance(const Locale
& aLocale
, UErrorCode
& success
)
846 return createInstance(TimeZone::createDefault(), aLocale
, success
);
849 // ------------------------------------- Adopting
851 // Note: this is the bottleneck that actually calls the service routines.
854 Calendar::makeInstance(const Locale
& aLocale
, UErrorCode
& success
) {
855 if (U_FAILURE(success
)) {
862 #if !UCONFIG_NO_SERVICE
863 if (isCalendarServiceUsed()) {
864 u
= getCalendarService(success
)->get(aLocale
, LocaleKey::KIND_ANY
, &actualLoc
, success
);
869 u
= createStandardCalendar(getCalendarTypeForLocale(aLocale
.getName()), aLocale
, success
);
873 if(U_FAILURE(success
) || !u
) {
874 if(U_SUCCESS(success
)) { // Propagate some kind of err
875 success
= U_INTERNAL_PROGRAM_ERROR
;
880 #if !UCONFIG_NO_SERVICE
881 const UnicodeString
* str
= dynamic_cast<const UnicodeString
*>(u
);
883 // It's a unicode string telling us what type of calendar to load ("gregorian", etc)
884 // Create a Locale over this string
886 LocaleUtility::initLocaleFromName(*str
, l
);
888 #ifdef U_DEBUG_CALSVC
889 fprintf(stderr
, "Calendar::createInstance(%s), looking up [%s]\n", aLocale
.getName(), l
.getName());
896 // Don't overwrite actualLoc, since the actual loc from this call
897 // may be something like "@calendar=gregorian" -- TODO investigate
899 c
= (Calendar
*)getCalendarService(success
)->get(l
, LocaleKey::KIND_ANY
, &actualLoc2
, success
);
901 if(U_FAILURE(success
) || !c
) {
902 if(U_SUCCESS(success
)) {
903 success
= U_INTERNAL_PROGRAM_ERROR
; // Propagate some err
908 str
= dynamic_cast<const UnicodeString
*>(c
);
910 // recursed! Second lookup returned a UnicodeString.
911 // Perhaps DefaultCalendar{} was set to another locale.
912 #ifdef U_DEBUG_CALSVC
914 // Extract a char* out of it..
915 int32_t len
= str
->length();
916 int32_t actLen
= sizeof(tmp
)-1;
920 str
->extract(0,len
,tmp
);
923 fprintf(stderr
, "err - recursed, 2nd lookup was unistring %s\n", tmp
);
925 success
= U_MISSING_RESOURCE_ERROR
; // requested a calendar type which could NOT be found.
929 #ifdef U_DEBUG_CALSVC
930 fprintf(stderr
, "%p: setting week count data to locale %s, actual locale %s\n", c
, (const char*)aLocale
.getName(), (const char *)actualLoc
.getName());
932 c
->setWeekData(aLocale
, c
->getType(), success
); // set the correct locale (this was an indirected calendar)
934 char keyword
[ULOC_FULLNAME_CAPACITY
];
935 UErrorCode tmpStatus
= U_ZERO_ERROR
;
936 l
.getKeywordValue("calendar", keyword
, ULOC_FULLNAME_CAPACITY
, tmpStatus
);
937 if (U_SUCCESS(tmpStatus
) && uprv_strcmp(keyword
, "iso8601") == 0) {
938 c
->setFirstDayOfWeek(UCAL_MONDAY
);
939 c
->setMinimalDaysInFirstWeek(4);
943 #endif /* UCONFIG_NO_SERVICE */
945 // a calendar was returned - we assume the factory did the right thing.
953 Calendar::createInstance(TimeZone
* zone
, const Locale
& aLocale
, UErrorCode
& success
)
955 LocalPointer
<TimeZone
> zonePtr(zone
);
956 const SharedCalendar
*shared
= NULL
;
957 UnifiedCache::getByLocale(aLocale
, shared
, success
);
958 if (U_FAILURE(success
)) {
961 Calendar
*c
= (*shared
)->clone();
964 success
= U_MEMORY_ALLOCATION_ERROR
;
968 // Now, reset calendar to default state:
969 c
->adoptTimeZone(zonePtr
.orphan()); // Set the correct time zone
970 c
->setTimeInMillis(getNow(), success
); // let the new calendar have the current time.
975 // -------------------------------------
978 Calendar::createInstance(const TimeZone
& zone
, const Locale
& aLocale
, UErrorCode
& success
)
980 Calendar
* c
= createInstance(aLocale
, success
);
981 if(U_SUCCESS(success
) && c
) {
982 c
->setTimeZone(zone
);
987 // -------------------------------------
990 Calendar::getCalendarTypeFromLocale(
991 const Locale
&aLocale
,
993 int32_t typeBufferSize
,
994 UErrorCode
&success
) {
995 const SharedCalendar
*shared
= NULL
;
996 UnifiedCache::getByLocale(aLocale
, shared
, success
);
997 if (U_FAILURE(success
)) {
1000 uprv_strncpy(typeBuffer
, (*shared
)->getType(), typeBufferSize
);
1001 shared
->removeRef();
1002 if (typeBuffer
[typeBufferSize
- 1]) {
1003 success
= U_BUFFER_OVERFLOW_ERROR
;
1008 Calendar::operator==(const Calendar
& that
) const
1010 UErrorCode status
= U_ZERO_ERROR
;
1011 return isEquivalentTo(that
) &&
1012 getTimeInMillis(status
) == that
.getTimeInMillis(status
) &&
1017 Calendar::isEquivalentTo(const Calendar
& other
) const
1019 return typeid(*this) == typeid(other
) &&
1020 fLenient
== other
.fLenient
&&
1021 fRepeatedWallTime
== other
.fRepeatedWallTime
&&
1022 fSkippedWallTime
== other
.fSkippedWallTime
&&
1023 fFirstDayOfWeek
== other
.fFirstDayOfWeek
&&
1024 fMinimalDaysInFirstWeek
== other
.fMinimalDaysInFirstWeek
&&
1025 fWeekendOnset
== other
.fWeekendOnset
&&
1026 fWeekendOnsetMillis
== other
.fWeekendOnsetMillis
&&
1027 fWeekendCease
== other
.fWeekendCease
&&
1028 fWeekendCeaseMillis
== other
.fWeekendCeaseMillis
&&
1029 *fZone
== *other
.fZone
;
1032 // -------------------------------------
1035 Calendar::equals(const Calendar
& when
, UErrorCode
& status
) const
1037 return (this == &when
||
1038 getTime(status
) == when
.getTime(status
));
1041 // -------------------------------------
1044 Calendar::before(const Calendar
& when
, UErrorCode
& status
) const
1046 return (this != &when
&&
1047 getTimeInMillis(status
) < when
.getTimeInMillis(status
));
1050 // -------------------------------------
1053 Calendar::after(const Calendar
& when
, UErrorCode
& status
) const
1055 return (this != &when
&&
1056 getTimeInMillis(status
) > when
.getTimeInMillis(status
));
1059 // -------------------------------------
1062 const Locale
* U_EXPORT2
1063 Calendar::getAvailableLocales(int32_t& count
)
1065 return Locale::getAvailableLocales(count
);
1068 // -------------------------------------
1070 StringEnumeration
* U_EXPORT2
1071 Calendar::getKeywordValuesForLocale(const char* key
,
1072 const Locale
& locale
, UBool commonlyUsed
, UErrorCode
& status
)
1074 // This is a wrapper over ucal_getKeywordValuesForLocale
1075 UEnumeration
*uenum
= ucal_getKeywordValuesForLocale(key
, locale
.getName(),
1076 commonlyUsed
, &status
);
1077 if (U_FAILURE(status
)) {
1081 return new UStringEnumeration(uenum
);
1084 // -------------------------------------
1089 return uprv_getUTCtime(); // return as milliseconds
1092 // -------------------------------------
1095 * Gets this Calendar's current time as a long.
1096 * @return the current time as UTC milliseconds from the epoch.
1099 Calendar::getTimeInMillis(UErrorCode
& status
) const
1101 if(U_FAILURE(status
))
1105 ((Calendar
*)this)->updateTime(status
);
1107 /* Test for buffer overflows */
1108 if(U_FAILURE(status
)) {
1114 // -------------------------------------
1117 * Sets this Calendar's current time from the given long value.
1118 * A status of U_ILLEGAL_ARGUMENT_ERROR is set when millis is
1119 * outside the range permitted by a Calendar object when not in lenient mode.
1120 * when in lenient mode the out of range values are pinned to their respective min/max.
1121 * @param date the new time in UTC milliseconds from the epoch.
1124 Calendar::setTimeInMillis( double millis
, UErrorCode
& status
) {
1125 if(U_FAILURE(status
))
1128 if (millis
> MAX_MILLIS
) {
1130 millis
= MAX_MILLIS
;
1132 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1135 } else if (millis
< MIN_MILLIS
) {
1137 millis
= MIN_MILLIS
;
1139 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1145 fAreFieldsSet
= fAreAllFieldsSet
= FALSE
;
1146 fIsTimeSet
= fAreFieldsVirtuallySet
= TRUE
;
1148 for (int32_t i
=0; i
<UCAL_FIELD_COUNT
; ++i
) {
1157 // -------------------------------------
1160 Calendar::get(UCalendarDateFields field
, UErrorCode
& status
) const
1162 // field values are only computed when actually requested; for more on when computation
1163 // of various things happens, see the "data flow in Calendar" description at the top
1165 if (U_SUCCESS(status
)) ((Calendar
*)this)->complete(status
); // Cast away const
1166 return U_SUCCESS(status
) ? fFields
[field
] : 0;
1169 // -------------------------------------
1172 Calendar::set(UCalendarDateFields field
, int32_t value
)
1174 if (fAreFieldsVirtuallySet
) {
1175 UErrorCode ec
= U_ZERO_ERROR
;
1178 fFields
[field
] = value
;
1179 /* Ensure that the fNextStamp value doesn't go pass max value for int32_t */
1180 if (fNextStamp
== STAMP_MAX
) {
1183 fStamp
[field
] = fNextStamp
++;
1184 fIsSet
[field
] = TRUE
; // Remove later
1185 fIsTimeSet
= fAreFieldsSet
= fAreFieldsVirtuallySet
= FALSE
;
1188 // -------------------------------------
1191 Calendar::set(int32_t year
, int32_t month
, int32_t date
)
1193 set(UCAL_YEAR
, year
);
1194 set(UCAL_MONTH
, month
);
1195 set(UCAL_DATE
, date
);
1198 // -------------------------------------
1201 Calendar::set(int32_t year
, int32_t month
, int32_t date
, int32_t hour
, int32_t minute
)
1203 set(UCAL_YEAR
, year
);
1204 set(UCAL_MONTH
, month
);
1205 set(UCAL_DATE
, date
);
1206 set(UCAL_HOUR_OF_DAY
, hour
);
1207 set(UCAL_MINUTE
, minute
);
1210 // -------------------------------------
1213 Calendar::set(int32_t year
, int32_t month
, int32_t date
, int32_t hour
, int32_t minute
, int32_t second
)
1215 set(UCAL_YEAR
, year
);
1216 set(UCAL_MONTH
, month
);
1217 set(UCAL_DATE
, date
);
1218 set(UCAL_HOUR_OF_DAY
, hour
);
1219 set(UCAL_MINUTE
, minute
);
1220 set(UCAL_SECOND
, second
);
1223 // -------------------------------------
1224 // For now the full getRelatedYear implementation is here;
1225 // per #10752 move the non-default implementation to subclasses
1226 // (default implementation will do no year adjustment)
1228 static int32_t gregoYearFromIslamicStart(int32_t year
) {
1229 // ad hoc conversion, improve under #10752
1230 // rough est for now, ok for grego 1846-2138,
1231 // otherwise occasionally wrong (for 3% of years)
1232 int cycle
, offset
, shift
= 0;
1234 cycle
= (year
- 1397) / 67;
1235 offset
= (year
- 1397) % 67;
1236 shift
= 2*cycle
+ ((offset
>= 33)? 1: 0);
1238 cycle
= (year
- 1396) / 67 - 1;
1239 offset
= -(year
- 1396) % 67;
1240 shift
= 2*cycle
+ ((offset
<= 33)? 1: 0);
1242 return year
+ 579 - shift
;
1245 int32_t Calendar::getRelatedYear(UErrorCode
&status
) const
1247 if (U_FAILURE(status
)) {
1250 int32_t year
= get(UCAL_EXTENDED_YEAR
, status
);
1251 if (U_FAILURE(status
)) {
1254 // modify for calendar type
1255 ECalType type
= getCalendarType(getType());
1257 case CALTYPE_PERSIAN
:
1259 case CALTYPE_HEBREW
:
1260 year
-= 3760; break;
1261 case CALTYPE_CHINESE
:
1262 year
-= 2637; break;
1263 case CALTYPE_INDIAN
:
1265 case CALTYPE_COPTIC
:
1267 case CALTYPE_ETHIOPIC
:
1269 case CALTYPE_ETHIOPIC_AMETE_ALEM
:
1272 year
-= 2333; break;
1273 case CALTYPE_ISLAMIC_CIVIL
:
1274 case CALTYPE_ISLAMIC
:
1275 case CALTYPE_ISLAMIC_UMALQURA
:
1276 case CALTYPE_ISLAMIC_TBLA
:
1277 case CALTYPE_ISLAMIC_RGSA
:
1278 year
= gregoYearFromIslamicStart(year
); break;
1280 // CALTYPE_GREGORIAN
1285 // do nothing, EXTENDED_YEAR same as Gregorian
1291 // -------------------------------------
1292 // For now the full setRelatedYear implementation is here;
1293 // per #10752 move the non-default implementation to subclasses
1294 // (default implementation will do no year adjustment)
1296 static int32_t firstIslamicStartYearFromGrego(int32_t year
) {
1297 // ad hoc conversion, improve under #10752
1298 // rough est for now, ok for grego 1846-2138,
1299 // otherwise occasionally wrong (for 3% of years)
1300 int cycle
, offset
, shift
= 0;
1302 cycle
= (year
- 1977) / 65;
1303 offset
= (year
- 1977) % 65;
1304 shift
= 2*cycle
+ ((offset
>= 32)? 1: 0);
1306 cycle
= (year
- 1976) / 65 - 1;
1307 offset
= -(year
- 1976) % 65;
1308 shift
= 2*cycle
+ ((offset
<= 32)? 1: 0);
1310 return year
- 579 + shift
;
1312 void Calendar::setRelatedYear(int32_t year
)
1314 // modify for calendar type
1315 ECalType type
= getCalendarType(getType());
1317 case CALTYPE_PERSIAN
:
1319 case CALTYPE_HEBREW
:
1320 year
+= 3760; break;
1321 case CALTYPE_CHINESE
:
1322 year
+= 2637; break;
1323 case CALTYPE_INDIAN
:
1325 case CALTYPE_COPTIC
:
1327 case CALTYPE_ETHIOPIC
:
1329 case CALTYPE_ETHIOPIC_AMETE_ALEM
:
1332 year
+= 2333; break;
1333 case CALTYPE_ISLAMIC_CIVIL
:
1334 case CALTYPE_ISLAMIC
:
1335 case CALTYPE_ISLAMIC_UMALQURA
:
1336 case CALTYPE_ISLAMIC_TBLA
:
1337 case CALTYPE_ISLAMIC_RGSA
:
1338 year
= firstIslamicStartYearFromGrego(year
); break;
1340 // CALTYPE_GREGORIAN
1345 // do nothing, EXTENDED_YEAR same as Gregorian
1348 // set extended year
1349 set(UCAL_EXTENDED_YEAR
, year
);
1352 // -------------------------------------
1357 // special behavior for chinese/dangi to set to beginning of current era;
1358 // need to do here and not in ChineseCalendar since clear is not virtual.
1360 if (dynamic_cast<const ChineseCalendar
*>(this)!=NULL
) {
1361 UErrorCode status
= U_ZERO_ERROR
;
1362 setTimeInMillis(getNow(), status
);
1363 eraNow
= get(UCAL_ERA
, status
); // sets 0 if error
1366 for (int32_t i
=0; i
<UCAL_FIELD_COUNT
; ++i
) {
1367 fFields
[i
] = 0; // Must do this; other code depends on it
1369 fIsSet
[i
] = FALSE
; // Remove later
1371 fIsTimeSet
= fAreFieldsSet
= fAreAllFieldsSet
= fAreFieldsVirtuallySet
= FALSE
;
1372 // fTime is not 'cleared' - may be used if no fields are set.
1374 set(UCAL_ERA
, eraNow
);
1378 // -------------------------------------
1381 Calendar::clear(UCalendarDateFields field
)
1383 if (fAreFieldsVirtuallySet
) {
1384 UErrorCode ec
= U_ZERO_ERROR
;
1388 fStamp
[field
] = kUnset
;
1389 fIsSet
[field
] = FALSE
; // Remove later
1390 fIsTimeSet
= fAreFieldsSet
= fAreAllFieldsSet
= fAreFieldsVirtuallySet
= FALSE
;
1393 // -------------------------------------
1396 Calendar::isSet(UCalendarDateFields field
) const
1398 return fAreFieldsVirtuallySet
|| (fStamp
[field
] != kUnset
);
1402 int32_t Calendar::newestStamp(UCalendarDateFields first
, UCalendarDateFields last
, int32_t bestStampSoFar
) const
1404 int32_t bestStamp
= bestStampSoFar
;
1405 for (int32_t i
=(int32_t)first
; i
<=(int32_t)last
; ++i
) {
1406 if (fStamp
[i
] > bestStamp
) {
1407 bestStamp
= fStamp
[i
];
1414 // -------------------------------------
1417 Calendar::complete(UErrorCode
& status
)
1421 /* Test for buffer overflows */
1422 if(U_FAILURE(status
)) {
1426 if (!fAreFieldsSet
) {
1427 computeFields(status
); // fills in unset fields
1428 /* Test for buffer overflows */
1429 if(U_FAILURE(status
)) {
1432 fAreFieldsSet
= TRUE
;
1433 fAreAllFieldsSet
= TRUE
;
1437 //-------------------------------------------------------------------------
1438 // Protected utility methods for use by subclasses. These are very handy
1439 // for implementing add, roll, and computeFields.
1440 //-------------------------------------------------------------------------
1443 * Adjust the specified field so that it is within
1444 * the allowable range for the date to which this calendar is set.
1445 * For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH}
1446 * field for a calendar set to April 31 would cause it to be set
1449 * <b>Subclassing:</b>
1451 * This utility method is intended for use by subclasses that need to implement
1452 * their own overrides of {@link #roll roll} and {@link #add add}.
1455 * <code>pinField</code> is implemented in terms of
1456 * {@link #getActualMinimum getActualMinimum}
1457 * and {@link #getActualMaximum getActualMaximum}. If either of those methods uses
1458 * a slow, iterative algorithm for a particular field, it would be
1459 * unwise to attempt to call <code>pinField</code> for that field. If you
1460 * really do need to do so, you should override this method to do
1461 * something more efficient for that field.
1463 * @param field The calendar field whose value should be pinned.
1465 * @see #getActualMinimum
1466 * @see #getActualMaximum
1469 void Calendar::pinField(UCalendarDateFields field
, UErrorCode
& status
) {
1470 int32_t max
= getActualMaximum(field
, status
);
1471 int32_t min
= getActualMinimum(field
, status
);
1473 if (fFields
[field
] > max
) {
1475 } else if (fFields
[field
] < min
) {
1481 void Calendar::computeFields(UErrorCode
&ec
)
1483 if (U_FAILURE(ec
)) {
1486 // Compute local wall millis
1487 double localMillis
= internalGetTime();
1488 int32_t rawOffset
, dstOffset
;
1489 getTimeZone().getOffset(localMillis
, FALSE
, rawOffset
, dstOffset
, ec
);
1490 localMillis
+= (rawOffset
+ dstOffset
);
1492 // Mark fields as set. Do this before calling handleComputeFields().
1493 uint32_t mask
= //fInternalSetMask;
1497 (1 << UCAL_DAY_OF_MONTH
) | // = UCAL_DATE
1498 (1 << UCAL_DAY_OF_YEAR
) |
1499 (1 << UCAL_EXTENDED_YEAR
);
1501 for (int32_t i
=0; i
<UCAL_FIELD_COUNT
; ++i
) {
1502 if ((mask
& 1) == 0) {
1503 fStamp
[i
] = kInternallySet
;
1504 fIsSet
[i
] = TRUE
; // Remove later
1507 fIsSet
[i
] = FALSE
; // Remove later
1512 // We used to check for and correct extreme millis values (near
1513 // Long.MIN_VALUE or Long.MAX_VALUE) here. Such values would cause
1514 // overflows from positive to negative (or vice versa) and had to
1515 // be manually tweaked. We no longer need to do this because we
1516 // have limited the range of supported dates to those that have a
1517 // Julian day that fits into an int. This allows us to implement a
1518 // JULIAN_DAY field and also removes some inelegant code. - Liu
1521 int32_t days
= (int32_t)ClockMath::floorDivide(localMillis
, (double)kOneDay
);
1523 internalSet(UCAL_JULIAN_DAY
,days
+ kEpochStartAsJulianDay
);
1525 #if defined (U_DEBUG_CAL)
1526 //fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n",
1527 //__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
1530 computeGregorianAndDOWFields(fFields
[UCAL_JULIAN_DAY
], ec
);
1532 // Call framework method to have subclass compute its fields.
1533 // These must include, at a minimum, MONTH, DAY_OF_MONTH,
1534 // EXTENDED_YEAR, YEAR, DAY_OF_YEAR. This method will call internalSet(),
1535 // which will update stamp[].
1536 handleComputeFields(fFields
[UCAL_JULIAN_DAY
], ec
);
1538 // Compute week-related fields, based on the subclass-computed
1539 // fields computed by handleComputeFields().
1540 computeWeekFields(ec
);
1542 // Compute time-related fields. These are indepent of the date and
1543 // of the subclass algorithm. They depend only on the local zone
1544 // wall milliseconds in day.
1545 int32_t millisInDay
= (int32_t) (localMillis
- (days
* kOneDay
));
1546 fFields
[UCAL_MILLISECONDS_IN_DAY
] = millisInDay
;
1547 fFields
[UCAL_MILLISECOND
] = millisInDay
% 1000;
1548 millisInDay
/= 1000;
1549 fFields
[UCAL_SECOND
] = millisInDay
% 60;
1551 fFields
[UCAL_MINUTE
] = millisInDay
% 60;
1553 fFields
[UCAL_HOUR_OF_DAY
] = millisInDay
;
1554 fFields
[UCAL_AM_PM
] = millisInDay
/ 12; // Assume AM == 0
1555 fFields
[UCAL_HOUR
] = millisInDay
% 12;
1556 fFields
[UCAL_ZONE_OFFSET
] = rawOffset
;
1557 fFields
[UCAL_DST_OFFSET
] = dstOffset
;
1560 uint8_t Calendar::julianDayToDayOfWeek(double julian
)
1562 // If julian is negative, then julian%7 will be negative, so we adjust
1563 // accordingly. We add 1 because Julian day 0 is Monday.
1564 int8_t dayOfWeek
= (int8_t) uprv_fmod(julian
+ 1, 7);
1566 uint8_t result
= (uint8_t)(dayOfWeek
+ ((dayOfWeek
< 0) ? (7+UCAL_SUNDAY
) : UCAL_SUNDAY
));
1571 * Compute the Gregorian calendar year, month, and day of month from
1572 * the given Julian day. These values are not stored in fields, but in
1573 * member variables gregorianXxx. Also compute the DAY_OF_WEEK and
1576 void Calendar::computeGregorianAndDOWFields(int32_t julianDay
, UErrorCode
&ec
)
1578 computeGregorianFields(julianDay
, ec
);
1580 // Compute day of week: JD 0 = Monday
1581 int32_t dow
= julianDayToDayOfWeek(julianDay
);
1582 internalSet(UCAL_DAY_OF_WEEK
,dow
);
1584 // Calculate 1-based localized day of week
1585 int32_t dowLocal
= dow
- getFirstDayOfWeek() + 1;
1589 internalSet(UCAL_DOW_LOCAL
,dowLocal
);
1590 fFields
[UCAL_DOW_LOCAL
] = dowLocal
;
1594 * Compute the Gregorian calendar year, month, and day of month from the
1595 * Julian day. These values are not stored in fields, but in member
1596 * variables gregorianXxx. They are used for time zone computations and by
1597 * subclasses that are Gregorian derivatives. Subclasses may call this
1598 * method to perform a Gregorian calendar millis->fields computation.
1600 void Calendar::computeGregorianFields(int32_t julianDay
, UErrorCode
& /* ec */) {
1601 int32_t gregorianDayOfWeekUnused
;
1602 Grego::dayToFields(julianDay
- kEpochStartAsJulianDay
, fGregorianYear
, fGregorianMonth
, fGregorianDayOfMonth
, gregorianDayOfWeekUnused
, fGregorianDayOfYear
);
1606 * Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH,
1607 * DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR,
1608 * DAY_OF_WEEK, and DAY_OF_YEAR. The latter fields are computed by the
1609 * subclass based on the calendar system.
1611 * <p>The YEAR_WOY field is computed simplistically. It is equal to YEAR
1612 * most of the time, but at the year boundary it may be adjusted to YEAR-1
1613 * or YEAR+1 to reflect the overlap of a week into an adjacent year. In
1614 * this case, a simple increment or decrement is performed on YEAR, even
1615 * though this may yield an invalid YEAR value. For instance, if the YEAR
1616 * is part of a calendar system with an N-year cycle field CYCLE, then
1617 * incrementing the YEAR may involve incrementing CYCLE and setting YEAR
1618 * back to 0 or 1. This is not handled by this code, and in fact cannot be
1619 * simply handled without having subclasses define an entire parallel set of
1620 * fields for fields larger than or equal to a year. This additional
1621 * complexity is not warranted, since the intention of the YEAR_WOY field is
1622 * to support ISO 8601 notation, so it will typically be used with a
1623 * proleptic Gregorian calendar, which has no field larger than a year.
1625 void Calendar::computeWeekFields(UErrorCode
&ec
) {
1629 int32_t eyear
= fFields
[UCAL_EXTENDED_YEAR
];
1630 int32_t dayOfWeek
= fFields
[UCAL_DAY_OF_WEEK
];
1631 int32_t dayOfYear
= fFields
[UCAL_DAY_OF_YEAR
];
1633 // WEEK_OF_YEAR start
1634 // Compute the week of the year. For the Gregorian calendar, valid week
1635 // numbers run from 1 to 52 or 53, depending on the year, the first day
1636 // of the week, and the minimal days in the first week. For other
1637 // calendars, the valid range may be different -- it depends on the year
1638 // length. Days at the start of the year may fall into the last week of
1639 // the previous year; days at the end of the year may fall into the
1640 // first week of the next year. ASSUME that the year length is less than
1642 int32_t yearOfWeekOfYear
= eyear
;
1643 int32_t relDow
= (dayOfWeek
+ 7 - getFirstDayOfWeek()) % 7; // 0..6
1644 int32_t relDowJan1
= (dayOfWeek
- dayOfYear
+ 7001 - getFirstDayOfWeek()) % 7; // 0..6
1645 int32_t woy
= (dayOfYear
- 1 + relDowJan1
) / 7; // 0..53
1646 if ((7 - relDowJan1
) >= getMinimalDaysInFirstWeek()) {
1650 // Adjust for weeks at the year end that overlap into the previous or
1651 // next calendar year.
1653 // We are the last week of the previous year.
1654 // Check to see if we are in the last week; if so, we need
1655 // to handle the case in which we are the first week of the
1658 int32_t prevDoy
= dayOfYear
+ handleGetYearLength(eyear
- 1);
1659 woy
= weekNumber(prevDoy
, dayOfWeek
);
1662 int32_t lastDoy
= handleGetYearLength(eyear
);
1663 // Fast check: For it to be week 1 of the next year, the DOY
1664 // must be on or after L-5, where L is yearLength(), then it
1665 // cannot possibly be week 1 of the next year:
1667 // doy: 359 360 361 362 363 364 365 001
1668 // dow: 1 2 3 4 5 6 7
1669 if (dayOfYear
>= (lastDoy
- 5)) {
1670 int32_t lastRelDow
= (relDow
+ lastDoy
- dayOfYear
) % 7;
1671 if (lastRelDow
< 0) {
1674 if (((6 - lastRelDow
) >= getMinimalDaysInFirstWeek()) &&
1675 ((dayOfYear
+ 7 - relDow
) > lastDoy
)) {
1681 fFields
[UCAL_WEEK_OF_YEAR
] = woy
;
1682 fFields
[UCAL_YEAR_WOY
] = yearOfWeekOfYear
;
1685 int32_t dayOfMonth
= fFields
[UCAL_DAY_OF_MONTH
];
1686 fFields
[UCAL_WEEK_OF_MONTH
] = weekNumber(dayOfMonth
, dayOfWeek
);
1687 fFields
[UCAL_DAY_OF_WEEK_IN_MONTH
] = (dayOfMonth
-1) / 7 + 1;
1688 #if defined (U_DEBUG_CAL)
1689 if(fFields
[UCAL_DAY_OF_WEEK_IN_MONTH
]==0) fprintf(stderr
, "%s:%d: DOWIM %d on %g\n",
1690 __FILE__
, __LINE__
,fFields
[UCAL_DAY_OF_WEEK_IN_MONTH
], fTime
);
1695 int32_t Calendar::weekNumber(int32_t desiredDay
, int32_t dayOfPeriod
, int32_t dayOfWeek
)
1697 // Determine the day of the week of the first day of the period
1698 // in question (either a year or a month). Zero represents the
1699 // first day of the week on this calendar.
1700 int32_t periodStartDayOfWeek
= (dayOfWeek
- getFirstDayOfWeek() - dayOfPeriod
+ 1) % 7;
1701 if (periodStartDayOfWeek
< 0) periodStartDayOfWeek
+= 7;
1703 // Compute the week number. Initially, ignore the first week, which
1704 // may be fractional (or may not be). We add periodStartDayOfWeek in
1705 // order to fill out the first week, if it is fractional.
1706 int32_t weekNo
= (desiredDay
+ periodStartDayOfWeek
- 1)/7;
1708 // If the first week is long enough, then count it. If
1709 // the minimal days in the first week is one, or if the period start
1710 // is zero, we always increment weekNo.
1711 if ((7 - periodStartDayOfWeek
) >= getMinimalDaysInFirstWeek()) ++weekNo
;
1716 void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode
&/* status */)
1718 internalSet(UCAL_MONTH
, getGregorianMonth());
1719 internalSet(UCAL_DAY_OF_MONTH
, getGregorianDayOfMonth());
1720 internalSet(UCAL_DAY_OF_YEAR
, getGregorianDayOfYear());
1721 int32_t eyear
= getGregorianYear();
1722 internalSet(UCAL_EXTENDED_YEAR
, eyear
);
1723 int32_t era
= GregorianCalendar::AD
;
1725 era
= GregorianCalendar::BC
;
1728 internalSet(UCAL_ERA
, era
);
1729 internalSet(UCAL_YEAR
, eyear
);
1731 // -------------------------------------
1734 void Calendar::roll(EDateFields field
, int32_t amount
, UErrorCode
& status
)
1736 roll((UCalendarDateFields
)field
, amount
, status
);
1739 void Calendar::roll(UCalendarDateFields field
, int32_t amount
, UErrorCode
& status
)
1742 return; // Nothing to do
1747 if(U_FAILURE(status
)) {
1751 case UCAL_DAY_OF_MONTH
:
1755 case UCAL_MILLISECOND
:
1756 case UCAL_MILLISECONDS_IN_DAY
:
1758 // These are the standard roll instructions. These work for all
1759 // simple cases, that is, cases in which the limits are fixed, such
1760 // as the hour, the day of the month, and the era.
1762 int32_t min
= getActualMinimum(field
,status
);
1763 int32_t max
= getActualMaximum(field
,status
);
1764 int32_t gap
= max
- min
+ 1;
1766 int32_t value
= internalGet(field
) + amount
;
1767 value
= (value
- min
) % gap
;
1778 case UCAL_HOUR_OF_DAY
:
1779 // Rolling the hour is difficult on the ONSET and CEASE days of
1780 // daylight savings. For example, if the change occurs at
1781 // 2 AM, we have the following progression:
1782 // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst
1783 // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std
1784 // To get around this problem we don't use fields; we manipulate
1785 // the time in millis directly.
1787 // Assume min == 0 in calculations below
1788 double start
= getTimeInMillis(status
);
1789 int32_t oldHour
= internalGet(field
);
1790 int32_t max
= getMaximum(field
);
1791 int32_t newHour
= (oldHour
+ amount
) % (max
+ 1);
1795 setTimeInMillis(start
+ kOneHour
* (newHour
- oldHour
),status
);
1800 // Rolling the month involves both pinning the final value
1801 // and adjusting the DAY_OF_MONTH if necessary. We only adjust the
1802 // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
1803 // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
1805 int32_t max
= getActualMaximum(UCAL_MONTH
, status
);
1806 int32_t mon
= (internalGet(UCAL_MONTH
) + amount
) % (max
+1);
1811 set(UCAL_MONTH
, mon
);
1813 // Keep the day of month in range. We don't want to spill over
1814 // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 ->
1816 pinField(UCAL_DAY_OF_MONTH
,status
);
1823 // * If era==0 and years go backwards in time, change sign of amount.
1824 // * Until we have new API per #9393, we temporarily hardcode knowledge of
1825 // which calendars have era 0 years that go backwards.
1826 UBool era0WithYearsThatGoBackwards
= FALSE
;
1827 int32_t era
= get(UCAL_ERA
, status
);
1829 const char * calType
= getType();
1830 if ( uprv_strcmp(calType
,"gregorian")==0 || uprv_strcmp(calType
,"roc")==0 || uprv_strcmp(calType
,"coptic")==0 ) {
1832 era0WithYearsThatGoBackwards
= TRUE
;
1835 int32_t newYear
= internalGet(field
) + amount
;
1836 if (era
> 0 || newYear
>= 1) {
1837 int32_t maxYear
= getActualMaximum(field
, status
);
1838 if (maxYear
< 32768) {
1839 // this era has real bounds, roll should wrap years
1841 newYear
= maxYear
- ((-newYear
) % maxYear
);
1842 } else if (newYear
> maxYear
) {
1843 newYear
= ((newYear
- 1) % maxYear
) + 1;
1845 // else era is unbounded, just pin low year instead of wrapping
1846 } else if (newYear
< 1) {
1849 // else we are in era 0 with newYear < 1;
1850 // calendars with years that go backwards must pin the year value at 0,
1851 // other calendars can have years < 0 in era 0
1852 } else if (era0WithYearsThatGoBackwards
) {
1855 set(field
, newYear
);
1856 pinField(UCAL_MONTH
,status
);
1857 pinField(UCAL_DAY_OF_MONTH
,status
);
1861 case UCAL_EXTENDED_YEAR
:
1862 // Rolling the year can involve pinning the DAY_OF_MONTH.
1863 set(field
, internalGet(field
) + amount
);
1864 pinField(UCAL_MONTH
,status
);
1865 pinField(UCAL_DAY_OF_MONTH
,status
);
1868 case UCAL_WEEK_OF_MONTH
:
1870 // This is tricky, because during the roll we may have to shift
1871 // to a different day of the week. For example:
1877 // When rolling from the 6th or 7th back one week, we go to the
1878 // 1st (assuming that the first partial week counts). The same
1879 // thing happens at the end of the month.
1881 // The other tricky thing is that we have to figure out whether
1882 // the first partial week actually counts or not, based on the
1883 // minimal first days in the week. And we have to use the
1884 // correct first day of the week to delineate the week
1887 // Here's our algorithm. First, we find the real boundaries of
1888 // the month. Then we discard the first partial week if it
1889 // doesn't count in this locale. Then we fill in the ends with
1890 // phantom days, so that the first partial week and the last
1891 // partial week are full weeks. We then have a nice square
1892 // block of weeks. We do the usual rolling within this block,
1893 // as is done elsewhere in this method. If we wind up on one of
1894 // the phantom days that we added, we recognize this and pin to
1895 // the first or the last day of the month. Easy, eh?
1897 // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
1898 // in this locale. We have dow in 0..6.
1899 int32_t dow
= internalGet(UCAL_DAY_OF_WEEK
) - getFirstDayOfWeek();
1900 if (dow
< 0) dow
+= 7;
1902 // Find the day of the week (normalized for locale) for the first
1904 int32_t fdm
= (dow
- internalGet(UCAL_DAY_OF_MONTH
) + 1) % 7;
1905 if (fdm
< 0) fdm
+= 7;
1907 // Get the first day of the first full week of the month,
1908 // including phantom days, if any. Figure out if the first week
1909 // counts or not; if it counts, then fill in phantom days. If
1910 // not, advance to the first real full week (skip the partial week).
1912 if ((7 - fdm
) < getMinimalDaysInFirstWeek())
1913 start
= 8 - fdm
; // Skip the first partial week
1915 start
= 1 - fdm
; // This may be zero or negative
1917 // Get the day of the week (normalized for locale) for the last
1918 // day of the month.
1919 int32_t monthLen
= getActualMaximum(UCAL_DAY_OF_MONTH
, status
);
1920 int32_t ldm
= (monthLen
- internalGet(UCAL_DAY_OF_MONTH
) + dow
) % 7;
1921 // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.
1923 // Get the limit day for the blocked-off rectangular month; that
1924 // is, the day which is one past the last day of the month,
1925 // after the month has already been filled in with phantom days
1926 // to fill out the last week. This day has a normalized DOW of 0.
1927 int32_t limit
= monthLen
+ 7 - ldm
;
1929 // Now roll between start and (limit - 1).
1930 int32_t gap
= limit
- start
;
1931 int32_t day_of_month
= (internalGet(UCAL_DAY_OF_MONTH
) + amount
*7 -
1933 if (day_of_month
< 0) day_of_month
+= gap
;
1934 day_of_month
+= start
;
1936 // Finally, pin to the real start and end of the month.
1937 if (day_of_month
< 1) day_of_month
= 1;
1938 if (day_of_month
> monthLen
) day_of_month
= monthLen
;
1940 // Set the DAY_OF_MONTH. We rely on the fact that this field
1941 // takes precedence over everything else (since all other fields
1942 // are also set at this point). If this fact changes (if the
1943 // disambiguation algorithm changes) then we will have to unset
1944 // the appropriate fields here so that DAY_OF_MONTH is attended
1946 set(UCAL_DAY_OF_MONTH
, day_of_month
);
1949 case UCAL_WEEK_OF_YEAR
:
1951 // This follows the outline of WEEK_OF_MONTH, except it applies
1952 // to the whole year. Please see the comment for WEEK_OF_MONTH
1953 // for general notes.
1955 // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
1956 // in this locale. We have dow in 0..6.
1957 int32_t dow
= internalGet(UCAL_DAY_OF_WEEK
) - getFirstDayOfWeek();
1958 if (dow
< 0) dow
+= 7;
1960 // Find the day of the week (normalized for locale) for the first
1962 int32_t fdy
= (dow
- internalGet(UCAL_DAY_OF_YEAR
) + 1) % 7;
1963 if (fdy
< 0) fdy
+= 7;
1965 // Get the first day of the first full week of the year,
1966 // including phantom days, if any. Figure out if the first week
1967 // counts or not; if it counts, then fill in phantom days. If
1968 // not, advance to the first real full week (skip the partial week).
1970 if ((7 - fdy
) < getMinimalDaysInFirstWeek())
1971 start
= 8 - fdy
; // Skip the first partial week
1973 start
= 1 - fdy
; // This may be zero or negative
1975 // Get the day of the week (normalized for locale) for the last
1977 int32_t yearLen
= getActualMaximum(UCAL_DAY_OF_YEAR
,status
);
1978 int32_t ldy
= (yearLen
- internalGet(UCAL_DAY_OF_YEAR
) + dow
) % 7;
1979 // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here.
1981 // Get the limit day for the blocked-off rectangular year; that
1982 // is, the day which is one past the last day of the year,
1983 // after the year has already been filled in with phantom days
1984 // to fill out the last week. This day has a normalized DOW of 0.
1985 int32_t limit
= yearLen
+ 7 - ldy
;
1987 // Now roll between start and (limit - 1).
1988 int32_t gap
= limit
- start
;
1989 int32_t day_of_year
= (internalGet(UCAL_DAY_OF_YEAR
) + amount
*7 -
1991 if (day_of_year
< 0) day_of_year
+= gap
;
1992 day_of_year
+= start
;
1994 // Finally, pin to the real start and end of the month.
1995 if (day_of_year
< 1) day_of_year
= 1;
1996 if (day_of_year
> yearLen
) day_of_year
= yearLen
;
1998 // Make sure that the year and day of year are attended to by
1999 // clearing other fields which would normally take precedence.
2000 // If the disambiguation algorithm is changed, this section will
2001 // have to be updated as well.
2002 set(UCAL_DAY_OF_YEAR
, day_of_year
);
2006 case UCAL_DAY_OF_YEAR
:
2008 // Roll the day of year using millis. Compute the millis for
2009 // the start of the year, and get the length of the year.
2010 double delta
= amount
* kOneDay
; // Scale up from days to millis
2011 double min2
= internalGet(UCAL_DAY_OF_YEAR
)-1;
2013 min2
= internalGetTime() - min2
;
2015 // double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay;
2018 double yearLength
= getActualMaximum(UCAL_DAY_OF_YEAR
,status
);
2019 double oneYear
= yearLength
;
2021 newtime
= uprv_fmod((internalGetTime() + delta
- min2
), oneYear
);
2022 if (newtime
< 0) newtime
+= oneYear
;
2023 setTimeInMillis(newtime
+ min2
, status
);
2026 case UCAL_DAY_OF_WEEK
:
2027 case UCAL_DOW_LOCAL
:
2029 // Roll the day of week using millis. Compute the millis for
2030 // the start of the week, using the first day of week setting.
2031 // Restrict the millis to [start, start+7days).
2032 double delta
= amount
* kOneDay
; // Scale up from days to millis
2033 // Compute the number of days before the current day in this
2034 // week. This will be a value 0..6.
2035 int32_t leadDays
= internalGet(field
);
2036 leadDays
-= (field
== UCAL_DAY_OF_WEEK
) ? getFirstDayOfWeek() : 1;
2037 if (leadDays
< 0) leadDays
+= 7;
2038 double min2
= internalGetTime() - leadDays
* kOneDay
;
2039 double newtime
= uprv_fmod((internalGetTime() + delta
- min2
), kOneWeek
);
2040 if (newtime
< 0) newtime
+= kOneWeek
;
2041 setTimeInMillis(newtime
+ min2
, status
);
2044 case UCAL_DAY_OF_WEEK_IN_MONTH
:
2046 // Roll the day of week in the month using millis. Determine
2047 // the first day of the week in the month, and then the last,
2048 // and then roll within that range.
2049 double delta
= amount
* kOneWeek
; // Scale up from weeks to millis
2050 // Find the number of same days of the week before this one
2052 int32_t preWeeks
= (internalGet(UCAL_DAY_OF_MONTH
) - 1) / 7;
2053 // Find the number of same days of the week after this one
2055 int32_t postWeeks
= (getActualMaximum(UCAL_DAY_OF_MONTH
,status
) -
2056 internalGet(UCAL_DAY_OF_MONTH
)) / 7;
2057 // From these compute the min and gap millis for rolling.
2058 double min2
= internalGetTime() - preWeeks
* kOneWeek
;
2059 double gap2
= kOneWeek
* (preWeeks
+ postWeeks
+ 1); // Must add 1!
2060 // Roll within this range
2061 double newtime
= uprv_fmod((internalGetTime() + delta
- min2
), gap2
);
2062 if (newtime
< 0) newtime
+= gap2
;
2063 setTimeInMillis(newtime
+ min2
, status
);
2066 case UCAL_JULIAN_DAY
:
2067 set(field
, internalGet(field
) + amount
);
2070 // Other fields cannot be rolled by this method
2071 #if defined (U_DEBUG_CAL)
2072 fprintf(stderr
, "%s:%d: ILLEGAL ARG because of roll on non-rollable field %s\n",
2073 __FILE__
, __LINE__
,fldName(field
));
2075 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2079 void Calendar::add(EDateFields field
, int32_t amount
, UErrorCode
& status
)
2081 Calendar::add((UCalendarDateFields
)field
, amount
, status
);
2084 // -------------------------------------
2085 void Calendar::add(UCalendarDateFields field
, int32_t amount
, UErrorCode
& status
)
2088 return; // Do nothing!
2091 // We handle most fields in the same way. The algorithm is to add
2092 // a computed amount of millis to the current millis. The only
2093 // wrinkle is with DST (and/or a change to the zone's UTC offset, which
2094 // we'll include with DST) -- for some fields, like the DAY_OF_MONTH,
2095 // we don't want the wall time to shift due to changes in DST. If the
2096 // result of the add operation is to move from DST to Standard, or
2097 // vice versa, we need to adjust by an hour forward or back,
2098 // respectively. For such fields we set keepWallTimeInvariant to TRUE.
2100 // We only adjust the DST for fields larger than an hour. For
2101 // fields smaller than an hour, we cannot adjust for DST without
2102 // causing problems. for instance, if you add one hour to April 5,
2103 // 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an
2104 // illegal value), but then the adjustment sees the change and
2105 // compensates by subtracting an hour. As a result the time
2106 // doesn't advance at all.
2108 // For some fields larger than a day, such as a UCAL_MONTH, we pin the
2109 // UCAL_DAY_OF_MONTH. This allows <March 31>.add(UCAL_MONTH, 1) to be
2110 // <April 30>, rather than <April 31> => <May 1>.
2112 double delta
= amount
; // delta in ms
2113 UBool keepWallTimeInvariant
= TRUE
;
2117 set(field
, get(field
, status
) + amount
);
2118 pinField(UCAL_ERA
, status
);
2124 // * If era=0 and years go backwards in time, change sign of amount.
2125 // * Until we have new API per #9393, we temporarily hardcode knowledge of
2126 // which calendars have era 0 years that go backwards.
2127 // * Note that for UCAL_YEAR (but not UCAL_YEAR_WOY) we could instead handle
2128 // this by applying the amount to the UCAL_EXTENDED_YEAR field; but since
2129 // we would still need to handle UCAL_YEAR_WOY as below, might as well
2130 // also handle UCAL_YEAR the same way.
2131 int32_t era
= get(UCAL_ERA
, status
);
2133 const char * calType
= getType();
2134 if ( uprv_strcmp(calType
,"gregorian")==0 || uprv_strcmp(calType
,"roc")==0 || uprv_strcmp(calType
,"coptic")==0 ) {
2139 // Fall through into normal handling
2141 case UCAL_EXTENDED_YEAR
:
2144 UBool oldLenient
= isLenient();
2146 set(field
, get(field
, status
) + amount
);
2147 pinField(UCAL_DAY_OF_MONTH
, status
);
2148 if(oldLenient
==FALSE
) {
2149 complete(status
); /* force recalculate */
2150 setLenient(oldLenient
);
2155 case UCAL_WEEK_OF_YEAR
:
2156 case UCAL_WEEK_OF_MONTH
:
2157 case UCAL_DAY_OF_WEEK_IN_MONTH
:
2162 delta
*= 12 * kOneHour
;
2165 case UCAL_DAY_OF_MONTH
:
2166 case UCAL_DAY_OF_YEAR
:
2167 case UCAL_DAY_OF_WEEK
:
2168 case UCAL_DOW_LOCAL
:
2169 case UCAL_JULIAN_DAY
:
2173 case UCAL_HOUR_OF_DAY
:
2176 keepWallTimeInvariant
= FALSE
;
2180 delta
*= kOneMinute
;
2181 keepWallTimeInvariant
= FALSE
;
2185 delta
*= kOneSecond
;
2186 keepWallTimeInvariant
= FALSE
;
2189 case UCAL_MILLISECOND
:
2190 case UCAL_MILLISECONDS_IN_DAY
:
2191 keepWallTimeInvariant
= FALSE
;
2195 #if defined (U_DEBUG_CAL)
2196 fprintf(stderr
, "%s:%d: ILLEGAL ARG because field %s not addable",
2197 __FILE__
, __LINE__
, fldName(field
));
2199 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2201 // throw new IllegalArgumentException("Calendar.add(" + fieldName(field) +
2202 // ") not supported");
2205 // In order to keep the wall time invariant (for fields where this is
2206 // appropriate), check the combined DST & ZONE offset before and
2207 // after the add() operation. If it changes, then adjust the millis
2209 int32_t prevOffset
= 0;
2210 int32_t prevWallTime
= 0;
2211 if (keepWallTimeInvariant
) {
2212 prevOffset
= get(UCAL_DST_OFFSET
, status
) + get(UCAL_ZONE_OFFSET
, status
);
2213 prevWallTime
= get(UCAL_MILLISECONDS_IN_DAY
, status
);
2216 setTimeInMillis(getTimeInMillis(status
) + delta
, status
);
2218 if (keepWallTimeInvariant
) {
2219 int32_t newWallTime
= get(UCAL_MILLISECONDS_IN_DAY
, status
);
2220 if (newWallTime
!= prevWallTime
) {
2221 // There is at least one zone transition between the base
2222 // time and the result time. As the result, wall time has
2224 UDate t
= internalGetTime();
2225 int32_t newOffset
= get(UCAL_DST_OFFSET
, status
) + get(UCAL_ZONE_OFFSET
, status
);
2226 if (newOffset
!= prevOffset
) {
2227 // When the difference of the previous UTC offset and
2228 // the new UTC offset exceeds 1 full day, we do not want
2229 // to roll over/back the date. For now, this only happens
2230 // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452.
2231 int32_t adjAmount
= prevOffset
- newOffset
;
2232 adjAmount
= adjAmount
>= 0 ? adjAmount
% (int32_t)kOneDay
: -(-adjAmount
% (int32_t)kOneDay
);
2233 if (adjAmount
!= 0) {
2234 setTimeInMillis(t
+ adjAmount
, status
);
2235 newWallTime
= get(UCAL_MILLISECONDS_IN_DAY
, status
);
2237 if (newWallTime
!= prevWallTime
) {
2238 // The result wall time or adjusted wall time was shifted because
2239 // the target wall time does not exist on the result date.
2240 switch (fSkippedWallTime
) {
2241 case UCAL_WALLTIME_FIRST
:
2242 if (adjAmount
> 0) {
2243 setTimeInMillis(t
, status
);
2246 case UCAL_WALLTIME_LAST
:
2247 if (adjAmount
< 0) {
2248 setTimeInMillis(t
, status
);
2251 case UCAL_WALLTIME_NEXT_VALID
:
2252 UDate tmpT
= adjAmount
> 0 ? internalGetTime() : t
;
2253 UDate immediatePrevTrans
;
2254 UBool hasTransition
= getImmediatePreviousZoneTransition(tmpT
, &immediatePrevTrans
, status
);
2255 if (U_SUCCESS(status
) && hasTransition
) {
2256 setTimeInMillis(immediatePrevTrans
, status
);
2266 // -------------------------------------
2267 int32_t Calendar::fieldDifference(UDate when
, EDateFields field
, UErrorCode
& status
) {
2268 return fieldDifference(when
, (UCalendarDateFields
) field
, status
);
2271 int32_t Calendar::fieldDifference(UDate targetMs
, UCalendarDateFields field
, UErrorCode
& ec
) {
2272 if (U_FAILURE(ec
)) return 0;
2274 double startMs
= getTimeInMillis(ec
);
2275 // Always add from the start millis. This accomodates
2276 // operations like adding years from February 29, 2000 up to
2277 // February 29, 2004. If 1, 1, 1, 1 is added to the year
2278 // field, the DOM gets pinned to 28 and stays there, giving an
2279 // incorrect DOM difference of 1. We have to add 1, reset, 2,
2280 // reset, 3, reset, 4.
2281 if (startMs
< targetMs
) {
2283 // Find a value that is too large
2284 while (U_SUCCESS(ec
)) {
2285 setTimeInMillis(startMs
, ec
);
2286 add(field
, max
, ec
);
2287 double ms
= getTimeInMillis(ec
);
2288 if (ms
== targetMs
) {
2290 } else if (ms
> targetMs
) {
2292 } else if (max
< INT32_MAX
) {
2299 // Field difference too large to fit into int32_t
2300 #if defined (U_DEBUG_CAL)
2301 fprintf(stderr
, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
2302 __FILE__
, __LINE__
, fldName(field
));
2304 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
2307 // Do a binary search
2308 while ((max
- min
) > 1 && U_SUCCESS(ec
)) {
2309 int32_t t
= min
+ (max
- min
)/2; // make sure intermediate values don't exceed INT32_MAX
2310 setTimeInMillis(startMs
, ec
);
2312 double ms
= getTimeInMillis(ec
);
2313 if (ms
== targetMs
) {
2315 } else if (ms
> targetMs
) {
2321 } else if (startMs
> targetMs
) {
2323 // Find a value that is too small
2324 while (U_SUCCESS(ec
)) {
2325 setTimeInMillis(startMs
, ec
);
2326 add(field
, max
, ec
);
2327 double ms
= getTimeInMillis(ec
);
2328 if (ms
== targetMs
) {
2330 } else if (ms
< targetMs
) {
2336 // Field difference too large to fit into int32_t
2337 #if defined (U_DEBUG_CAL)
2338 fprintf(stderr
, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
2339 __FILE__
, __LINE__
, fldName(field
));
2341 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
2345 // Do a binary search
2346 while ((min
- max
) > 1 && U_SUCCESS(ec
)) {
2347 int32_t t
= min
+ (max
- min
)/2; // make sure intermediate values don't exceed INT32_MAX
2348 setTimeInMillis(startMs
, ec
);
2350 double ms
= getTimeInMillis(ec
);
2351 if (ms
== targetMs
) {
2353 } else if (ms
< targetMs
) {
2360 // Set calendar to end point
2361 setTimeInMillis(startMs
, ec
);
2362 add(field
, min
, ec
);
2364 /* Test for buffer overflows */
2371 // -------------------------------------
2374 Calendar::adoptTimeZone(TimeZone
* zone
)
2376 // Do nothing if passed-in zone is NULL
2377 if (zone
== NULL
) return;
2379 // fZone should always be non-null
2383 // if the zone changes, we need to recompute the time fields
2384 fAreFieldsSet
= FALSE
;
2387 // -------------------------------------
2389 Calendar::setTimeZone(const TimeZone
& zone
)
2391 adoptTimeZone(zone
.clone());
2394 // -------------------------------------
2397 Calendar::getTimeZone() const
2399 U_ASSERT(fZone
!= NULL
);
2403 // -------------------------------------
2406 Calendar::orphanTimeZone()
2408 // we let go of the time zone; the new time zone is the system default time zone
2409 TimeZone
*defaultZone
= TimeZone::createDefault();
2410 if (defaultZone
== NULL
) {
2411 // No error handling available. Must keep fZone non-NULL, there are many unchecked uses.
2414 TimeZone
*z
= fZone
;
2415 fZone
= defaultZone
;
2419 // -------------------------------------
2422 Calendar::setLenient(UBool lenient
)
2427 // -------------------------------------
2430 Calendar::isLenient() const
2435 // -------------------------------------
2438 Calendar::setRepeatedWallTimeOption(UCalendarWallTimeOption option
)
2440 if (option
== UCAL_WALLTIME_LAST
|| option
== UCAL_WALLTIME_FIRST
) {
2441 fRepeatedWallTime
= option
;
2445 // -------------------------------------
2447 UCalendarWallTimeOption
2448 Calendar::getRepeatedWallTimeOption(void) const
2450 return fRepeatedWallTime
;
2453 // -------------------------------------
2456 Calendar::setSkippedWallTimeOption(UCalendarWallTimeOption option
)
2458 fSkippedWallTime
= option
;
2461 // -------------------------------------
2463 UCalendarWallTimeOption
2464 Calendar::getSkippedWallTimeOption(void) const
2466 return fSkippedWallTime
;
2469 // -------------------------------------
2472 Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value
)
2474 if (fFirstDayOfWeek
!= value
&&
2475 value
>= UCAL_SUNDAY
&& value
<= UCAL_SATURDAY
) {
2476 fFirstDayOfWeek
= value
;
2477 fAreFieldsSet
= FALSE
;
2481 // -------------------------------------
2483 Calendar::EDaysOfWeek
2484 Calendar::getFirstDayOfWeek() const
2486 return (Calendar::EDaysOfWeek
)fFirstDayOfWeek
;
2490 Calendar::getFirstDayOfWeek(UErrorCode
& /*status*/) const
2492 return fFirstDayOfWeek
;
2494 // -------------------------------------
2497 Calendar::setMinimalDaysInFirstWeek(uint8_t value
)
2499 // Values less than 1 have the same effect as 1; values greater
2500 // than 7 have the same effect as 7. However, we normalize values
2501 // so operator== and so forth work.
2504 } else if (value
> 7) {
2507 if (fMinimalDaysInFirstWeek
!= value
) {
2508 fMinimalDaysInFirstWeek
= value
;
2509 fAreFieldsSet
= FALSE
;
2513 // -------------------------------------
2516 Calendar::getMinimalDaysInFirstWeek() const
2518 return fMinimalDaysInFirstWeek
;
2521 // -------------------------------------
2522 // weekend functions, just dummy implementations for now (for API freeze)
2524 UCalendarWeekdayType
2525 Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek
, UErrorCode
&status
) const
2527 if (U_FAILURE(status
)) {
2528 return UCAL_WEEKDAY
;
2530 if (dayOfWeek
< UCAL_SUNDAY
|| dayOfWeek
> UCAL_SATURDAY
) {
2531 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2532 return UCAL_WEEKDAY
;
2534 if (fWeekendOnset
== fWeekendCease
) {
2535 if (dayOfWeek
!= fWeekendOnset
)
2536 return UCAL_WEEKDAY
;
2537 return (fWeekendOnsetMillis
== 0) ? UCAL_WEEKEND
: UCAL_WEEKEND_ONSET
;
2539 if (fWeekendOnset
< fWeekendCease
) {
2540 if (dayOfWeek
< fWeekendOnset
|| dayOfWeek
> fWeekendCease
) {
2541 return UCAL_WEEKDAY
;
2544 if (dayOfWeek
> fWeekendCease
&& dayOfWeek
< fWeekendOnset
) {
2545 return UCAL_WEEKDAY
;
2548 if (dayOfWeek
== fWeekendOnset
) {
2549 return (fWeekendOnsetMillis
== 0) ? UCAL_WEEKEND
: UCAL_WEEKEND_ONSET
;
2551 if (dayOfWeek
== fWeekendCease
) {
2552 return (fWeekendCeaseMillis
>= 86400000) ? UCAL_WEEKEND
: UCAL_WEEKEND_CEASE
;
2554 return UCAL_WEEKEND
;
2558 Calendar::getWeekendTransition(UCalendarDaysOfWeek dayOfWeek
, UErrorCode
&status
) const
2560 if (U_FAILURE(status
)) {
2563 if (dayOfWeek
== fWeekendOnset
) {
2564 return fWeekendOnsetMillis
;
2565 } else if (dayOfWeek
== fWeekendCease
) {
2566 return fWeekendCeaseMillis
;
2568 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2573 Calendar::isWeekend(UDate date
, UErrorCode
&status
) const
2575 if (U_FAILURE(status
)) {
2578 // clone the calendar so we don't mess with the real one.
2579 Calendar
*work
= (Calendar
*)this->clone();
2581 status
= U_MEMORY_ALLOCATION_ERROR
;
2584 UBool result
= FALSE
;
2585 work
->setTime(date
, status
);
2586 if (U_SUCCESS(status
)) {
2587 result
= work
->isWeekend();
2594 Calendar::isWeekend(void) const
2596 UErrorCode status
= U_ZERO_ERROR
;
2597 UCalendarDaysOfWeek dayOfWeek
= (UCalendarDaysOfWeek
)get(UCAL_DAY_OF_WEEK
, status
);
2598 UCalendarWeekdayType dayType
= getDayOfWeekType(dayOfWeek
, status
);
2599 if (U_SUCCESS(status
)) {
2605 case UCAL_WEEKEND_ONSET
:
2606 case UCAL_WEEKEND_CEASE
:
2607 // Use internalGet() because the above call to get() populated all fields.
2609 int32_t millisInDay
= internalGet(UCAL_MILLISECONDS_IN_DAY
);
2610 int32_t transitionMillis
= getWeekendTransition(dayOfWeek
, status
);
2611 if (U_SUCCESS(status
)) {
2612 return (dayType
== UCAL_WEEKEND_ONSET
)?
2613 (millisInDay
>= transitionMillis
):
2614 (millisInDay
< transitionMillis
);
2616 // else fall through, return FALSE
2626 // ------------------------------------- limits
2629 Calendar::getMinimum(EDateFields field
) const {
2630 return getLimit((UCalendarDateFields
) field
,UCAL_LIMIT_MINIMUM
);
2634 Calendar::getMinimum(UCalendarDateFields field
) const
2636 return getLimit(field
,UCAL_LIMIT_MINIMUM
);
2639 // -------------------------------------
2641 Calendar::getMaximum(EDateFields field
) const
2643 return getLimit((UCalendarDateFields
) field
,UCAL_LIMIT_MAXIMUM
);
2647 Calendar::getMaximum(UCalendarDateFields field
) const
2649 return getLimit(field
,UCAL_LIMIT_MAXIMUM
);
2652 // -------------------------------------
2654 Calendar::getGreatestMinimum(EDateFields field
) const
2656 return getLimit((UCalendarDateFields
)field
,UCAL_LIMIT_GREATEST_MINIMUM
);
2660 Calendar::getGreatestMinimum(UCalendarDateFields field
) const
2662 return getLimit(field
,UCAL_LIMIT_GREATEST_MINIMUM
);
2665 // -------------------------------------
2667 Calendar::getLeastMaximum(EDateFields field
) const
2669 return getLimit((UCalendarDateFields
) field
,UCAL_LIMIT_LEAST_MAXIMUM
);
2673 Calendar::getLeastMaximum(UCalendarDateFields field
) const
2675 return getLimit( field
,UCAL_LIMIT_LEAST_MAXIMUM
);
2678 // -------------------------------------
2680 Calendar::getActualMinimum(EDateFields field
, UErrorCode
& status
) const
2682 return getActualMinimum((UCalendarDateFields
) field
, status
);
2685 int32_t Calendar::getLimit(UCalendarDateFields field
, ELimitType limitType
) const {
2687 case UCAL_DAY_OF_WEEK
:
2690 case UCAL_HOUR_OF_DAY
:
2693 case UCAL_MILLISECOND
:
2694 case UCAL_ZONE_OFFSET
:
2695 case UCAL_DST_OFFSET
:
2696 case UCAL_DOW_LOCAL
:
2697 case UCAL_JULIAN_DAY
:
2698 case UCAL_MILLISECONDS_IN_DAY
:
2699 case UCAL_IS_LEAP_MONTH
:
2700 return kCalendarLimits
[field
][limitType
];
2702 case UCAL_WEEK_OF_MONTH
:
2705 if (limitType
== UCAL_LIMIT_MINIMUM
) {
2706 limit
= getMinimalDaysInFirstWeek() == 1 ? 1 : 0;
2707 } else if (limitType
== UCAL_LIMIT_GREATEST_MINIMUM
) {
2710 int32_t minDaysInFirst
= getMinimalDaysInFirstWeek();
2711 int32_t daysInMonth
= handleGetLimit(UCAL_DAY_OF_MONTH
, limitType
);
2712 if (limitType
== UCAL_LIMIT_LEAST_MAXIMUM
) {
2713 limit
= (daysInMonth
+ (7 - minDaysInFirst
)) / 7;
2714 } else { // limitType == UCAL_LIMIT_MAXIMUM
2715 limit
= (daysInMonth
+ 6 + (7 - minDaysInFirst
)) / 7;
2721 return handleGetLimit(field
, limitType
);
2727 Calendar::getActualMinimum(UCalendarDateFields field
, UErrorCode
& status
) const
2729 int32_t fieldValue
= getGreatestMinimum(field
);
2730 int32_t endValue
= getMinimum(field
);
2732 // if we know that the minimum value is always the same, just return it
2733 if (fieldValue
== endValue
) {
2737 // clone the calendar so we don't mess with the real one, and set it to
2738 // accept anything for the field values
2739 Calendar
*work
= (Calendar
*)this->clone();
2741 status
= U_MEMORY_ALLOCATION_ERROR
;
2744 work
->setLenient(TRUE
);
2746 // now try each value from getLeastMaximum() to getMaximum() one by one until
2747 // we get a value that normalizes to another value. The last value that
2748 // normalizes to itself is the actual minimum for the current date
2749 int32_t result
= fieldValue
;
2752 work
->set(field
, fieldValue
);
2753 if (work
->get(field
, status
) != fieldValue
) {
2757 result
= fieldValue
;
2760 } while (fieldValue
>= endValue
);
2764 /* Test for buffer overflows */
2765 if(U_FAILURE(status
)) {
2771 // -------------------------------------
2776 * Ensure that each field is within its valid range by calling {@link
2777 * #validateField(int)} on each field that has been set. This method
2778 * should only be called if this calendar is not lenient.
2780 * @see #validateField(int)
2782 void Calendar::validateFields(UErrorCode
&status
) {
2783 for (int32_t field
= 0; U_SUCCESS(status
) && (field
< UCAL_FIELD_COUNT
); field
++) {
2784 if (fStamp
[field
] >= kMinimumUserStamp
) {
2785 validateField((UCalendarDateFields
)field
, status
);
2791 * Validate a single field of this calendar. Subclasses should
2792 * override this method to validate any calendar-specific fields.
2793 * Generic fields can be handled by
2794 * <code>Calendar.validateField()</code>.
2795 * @see #validateField(int, int, int)
2797 void Calendar::validateField(UCalendarDateFields field
, UErrorCode
&status
) {
2800 case UCAL_DAY_OF_MONTH
:
2801 y
= handleGetExtendedYear();
2802 validateField(field
, 1, handleGetMonthLength(y
, internalGet(UCAL_MONTH
)), status
);
2804 case UCAL_DAY_OF_YEAR
:
2805 y
= handleGetExtendedYear();
2806 validateField(field
, 1, handleGetYearLength(y
), status
);
2808 case UCAL_DAY_OF_WEEK_IN_MONTH
:
2809 if (internalGet(field
) == 0) {
2810 #if defined (U_DEBUG_CAL)
2811 fprintf(stderr
, "%s:%d: ILLEGAL ARG because DOW in month cannot be 0\n",
2812 __FILE__
, __LINE__
);
2814 status
= U_ILLEGAL_ARGUMENT_ERROR
; // "DAY_OF_WEEK_IN_MONTH cannot be zero"
2817 validateField(field
, getMinimum(field
), getMaximum(field
), status
);
2820 validateField(field
, getMinimum(field
), getMaximum(field
), status
);
2826 * Validate a single field of this calendar given its minimum and
2827 * maximum allowed value. If the field is out of range, throw a
2828 * descriptive <code>IllegalArgumentException</code>. Subclasses may
2829 * use this method in their implementation of {@link
2830 * #validateField(int)}.
2832 void Calendar::validateField(UCalendarDateFields field
, int32_t min
, int32_t max
, UErrorCode
& status
)
2834 int32_t value
= fFields
[field
];
2835 if (value
< min
|| value
> max
) {
2836 #if defined (U_DEBUG_CAL)
2837 fprintf(stderr
, "%s:%d: ILLEGAL ARG because of field %s out of range %d..%d at %d\n",
2838 __FILE__
, __LINE__
,fldName(field
),min
,max
,value
);
2840 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2845 // -------------------------
2847 const UFieldResolutionTable
* Calendar::getFieldResolutionTable() const {
2848 return kDatePrecedence
;
2852 UCalendarDateFields
Calendar::newerField(UCalendarDateFields defaultField
, UCalendarDateFields alternateField
) const
2854 if (fStamp
[alternateField
] > fStamp
[defaultField
]) {
2855 return alternateField
;
2857 return defaultField
;
2860 UCalendarDateFields
Calendar::resolveFields(const UFieldResolutionTable
* precedenceTable
) {
2861 int32_t bestField
= UCAL_FIELD_COUNT
;
2862 int32_t tempBestField
;
2863 UBool restoreWeekOfInternalSet
= FALSE
;
2864 if (fStamp
[UCAL_DAY_OF_WEEK
] >= kMinimumUserStamp
&&
2865 fStamp
[UCAL_DATE
] >= kMinimumUserStamp
&&
2866 fStamp
[UCAL_MONTH
] >= kMinimumUserStamp
&&
2867 fStamp
[UCAL_WEEK_OF_YEAR
] == kInternallySet
&&
2868 fStamp
[UCAL_WEEK_OF_MONTH
] == kInternallySet
&&
2869 fStamp
[UCAL_DAY_OF_WEEK_IN_MONTH
] == kInternallySet
) {
2870 int32_t monthStampDelta
= fStamp
[UCAL_DAY_OF_WEEK
] - fStamp
[UCAL_MONTH
];
2871 int32_t dateStampDelta
= fStamp
[UCAL_DAY_OF_WEEK
] - fStamp
[UCAL_DATE
];
2872 if ( monthStampDelta
>= 1 && monthStampDelta
<= 3 && dateStampDelta
>= 1 && dateStampDelta
<= 3 ) {
2873 // If UCAL_MONTH, UCAL_DATE and UCAL_DAY_OF_WEEK are all explicitly set nearly one after the
2874 // other (as when parsing a single date format), with UCAL_DAY_OF_WEEK set most recently, and
2875 // if UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, and UCAL_DAY_OF_WEEK_IN_MONTH are all only
2876 // implicitly set (as from setTimeInMillis), then for the calculations in this call temporarily
2877 // treat UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, and UCAL_DAY_OF_WEEK_IN_MONTH as unset so they
2878 // don't combine with UCAL_DAY_OF_WEEK to override the date in UCAL_MONTH & UCAL_DATE. All of
2879 // these conditions are to avoid messing up the case of parsing a format with UCAL_DAY_OF_WEEK
2880 // alone or in combination with other fields besides UCAL_MONTH, UCAL_DATE. Note: the current
2881 // stamp value is incremented each time Calendar::set is called to explicitly set a field value.
2882 fStamp
[UCAL_WEEK_OF_YEAR
] = kUnset
;
2883 fStamp
[UCAL_WEEK_OF_MONTH
] = kUnset
;
2884 fStamp
[UCAL_DAY_OF_WEEK_IN_MONTH
] = kUnset
;
2885 restoreWeekOfInternalSet
= TRUE
;
2888 for (int32_t g
=0; precedenceTable
[g
][0][0] != -1 && (bestField
== UCAL_FIELD_COUNT
); ++g
) {
2889 int32_t bestStamp
= kUnset
;
2890 for (int32_t l
=0; precedenceTable
[g
][l
][0] != -1; ++l
) {
2891 int32_t lineStamp
= kUnset
;
2892 // Skip over first entry if it is negative
2893 for (int32_t i
=((precedenceTable
[g
][l
][0]>=kResolveRemap
)?1:0); precedenceTable
[g
][l
][i
]!=-1; ++i
) {
2894 U_ASSERT(precedenceTable
[g
][l
][i
] < UCAL_FIELD_COUNT
);
2895 int32_t s
= fStamp
[precedenceTable
[g
][l
][i
]];
2896 // If any field is unset then don't use this line
2899 } else if(s
> lineStamp
) {
2903 // Record new maximum stamp & field no.
2904 if (lineStamp
> bestStamp
) {
2905 tempBestField
= precedenceTable
[g
][l
][0]; // First field refers to entire line
2906 if (tempBestField
>= kResolveRemap
) {
2907 tempBestField
&= (kResolveRemap
-1);
2908 // This check is needed to resolve some issues with UCAL_YEAR precedence mapping
2909 if (tempBestField
!= UCAL_DATE
|| (fStamp
[UCAL_WEEK_OF_MONTH
] < fStamp
[tempBestField
])) {
2910 bestField
= tempBestField
;
2913 bestField
= tempBestField
;
2916 if (bestField
== tempBestField
) {
2917 bestStamp
= lineStamp
;
2924 if (restoreWeekOfInternalSet
) {
2925 // Restore the field stamps temporarily unset above.
2926 fStamp
[UCAL_WEEK_OF_YEAR
] = kInternallySet
;
2927 fStamp
[UCAL_WEEK_OF_MONTH
] = kInternallySet
;
2928 fStamp
[UCAL_DAY_OF_WEEK_IN_MONTH
] = kInternallySet
;
2930 return (UCalendarDateFields
)bestField
;
2933 const UFieldResolutionTable
Calendar::kDatePrecedence
[] =
2936 { UCAL_DAY_OF_MONTH
, kResolveSTOP
},
2937 { UCAL_WEEK_OF_YEAR
, UCAL_DAY_OF_WEEK
, kResolveSTOP
},
2938 { UCAL_WEEK_OF_MONTH
, UCAL_DAY_OF_WEEK
, kResolveSTOP
},
2939 { UCAL_DAY_OF_WEEK_IN_MONTH
, UCAL_DAY_OF_WEEK
, kResolveSTOP
},
2940 { UCAL_WEEK_OF_YEAR
, UCAL_DOW_LOCAL
, kResolveSTOP
},
2941 { UCAL_WEEK_OF_MONTH
, UCAL_DOW_LOCAL
, kResolveSTOP
},
2942 { UCAL_DAY_OF_WEEK_IN_MONTH
, UCAL_DOW_LOCAL
, kResolveSTOP
},
2943 { UCAL_DAY_OF_YEAR
, kResolveSTOP
},
2944 { kResolveRemap
| UCAL_DAY_OF_MONTH
, UCAL_YEAR
, kResolveSTOP
}, // if YEAR is set over YEAR_WOY use DAY_OF_MONTH
2945 { kResolveRemap
| UCAL_WEEK_OF_YEAR
, UCAL_YEAR_WOY
, kResolveSTOP
}, // if YEAR_WOY is set, calc based on WEEK_OF_YEAR
2949 { UCAL_WEEK_OF_YEAR
, kResolveSTOP
},
2950 { UCAL_WEEK_OF_MONTH
, kResolveSTOP
},
2951 { UCAL_DAY_OF_WEEK_IN_MONTH
, kResolveSTOP
},
2952 { kResolveRemap
| UCAL_DAY_OF_WEEK_IN_MONTH
, UCAL_DAY_OF_WEEK
, kResolveSTOP
},
2953 { kResolveRemap
| UCAL_DAY_OF_WEEK_IN_MONTH
, UCAL_DOW_LOCAL
, kResolveSTOP
},
2960 const UFieldResolutionTable
Calendar::kDOWPrecedence
[] =
2963 { UCAL_DAY_OF_WEEK
,kResolveSTOP
, kResolveSTOP
},
2964 { UCAL_DOW_LOCAL
,kResolveSTOP
, kResolveSTOP
},
2970 // precedence for calculating a year
2971 const UFieldResolutionTable
Calendar::kYearPrecedence
[] =
2974 { UCAL_YEAR
, kResolveSTOP
},
2975 { UCAL_EXTENDED_YEAR
, kResolveSTOP
},
2976 { UCAL_YEAR_WOY
, UCAL_WEEK_OF_YEAR
, kResolveSTOP
}, // YEAR_WOY is useless without WEEK_OF_YEAR
2983 // -------------------------
2986 void Calendar::computeTime(UErrorCode
& status
) {
2988 validateFields(status
);
2989 if (U_FAILURE(status
)) {
2994 // Compute the Julian day
2995 int32_t julianDay
= computeJulianDay();
2997 double millis
= Grego::julianDayToMillis(julianDay
);
2999 #if defined (U_DEBUG_CAL)
3000 // int32_t julianInsanityCheck = (int32_t)ClockMath::floorDivide(millis, kOneDay);
3001 // julianInsanityCheck += kEpochStartAsJulianDay;
3002 // if(1 || julianInsanityCheck != julianDay) {
3003 // fprintf(stderr, "%s:%d- D'oh- computed jules %d, to mills (%s)%.lf, recomputed %d\n",
3004 // __FILE__, __LINE__, julianDay, millis<0.0?"NEG":"", millis, julianInsanityCheck);
3008 int32_t millisInDay
;
3010 // We only use MILLISECONDS_IN_DAY if it has been set by the user.
3011 // This makes it possible for the caller to set the calendar to a
3012 // time and call clear(MONTH) to reset the MONTH to January. This
3013 // is legacy behavior. Without this, clear(MONTH) has no effect,
3014 // since the internally set JULIAN_DAY is used.
3015 if (fStamp
[UCAL_MILLISECONDS_IN_DAY
] >= ((int32_t)kMinimumUserStamp
) &&
3016 newestStamp(UCAL_AM_PM
, UCAL_MILLISECOND
, kUnset
) <= fStamp
[UCAL_MILLISECONDS_IN_DAY
]) {
3017 millisInDay
= internalGet(UCAL_MILLISECONDS_IN_DAY
);
3019 millisInDay
= computeMillisInDay();
3023 if (fStamp
[UCAL_ZONE_OFFSET
] >= ((int32_t)kMinimumUserStamp
) || fStamp
[UCAL_DST_OFFSET
] >= ((int32_t)kMinimumUserStamp
)) {
3024 t
= millis
+ millisInDay
- (internalGet(UCAL_ZONE_OFFSET
) + internalGet(UCAL_DST_OFFSET
));
3026 // Compute the time zone offset and DST offset. There are two potential
3027 // ambiguities here. We'll assume a 2:00 am (wall time) switchover time
3028 // for discussion purposes here.
3030 // 1. The positive offset change such as transition into DST.
3031 // Here, a designated time of 2:00 am - 2:59 am does not actually exist.
3032 // For this case, skippedWallTime option specifies the behavior.
3033 // For example, 2:30 am is interpreted as;
3034 // - WALLTIME_LAST(default): 3:30 am (DST) (interpreting 2:30 am as 31 minutes after 1:59 am (STD))
3035 // - WALLTIME_FIRST: 1:30 am (STD) (interpreting 2:30 am as 30 minutes before 3:00 am (DST))
3036 // - WALLTIME_NEXT_VALID: 3:00 am (DST) (next valid time after 2:30 am on a wall clock)
3037 // 2. The negative offset change such as transition out of DST.
3038 // Here, a designated time of 1:00 am - 1:59 am can be in standard or DST. Both are valid
3039 // representations (the rep jumps from 1:59:59 DST to 1:00:00 Std).
3040 // For this case, repeatedWallTime option specifies the behavior.
3041 // For example, 1:30 am is interpreted as;
3042 // - WALLTIME_LAST(default): 1:30 am (STD) - latter occurrence
3043 // - WALLTIME_FIRST: 1:30 am (DST) - former occurrence
3045 // In addition to above, when calendar is strict (not default), wall time falls into
3046 // the skipped time range will be processed as an error case.
3048 // These special cases are mostly handled in #computeZoneOffset(long), except WALLTIME_NEXT_VALID
3049 // at positive offset change. The protected method computeZoneOffset(long) is exposed to Calendar
3050 // subclass implementations and marked as @stable. Strictly speaking, WALLTIME_NEXT_VALID
3051 // should be also handled in the same place, but we cannot change the code flow without deprecating
3052 // the protected method.
3054 // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
3055 // or DST_OFFSET fields; then we use those fields.
3057 if (!isLenient() || fSkippedWallTime
== UCAL_WALLTIME_NEXT_VALID
) {
3058 // When strict, invalidate a wall time falls into a skipped wall time range.
3059 // When lenient and skipped wall time option is WALLTIME_NEXT_VALID,
3060 // the result time will be adjusted to the next valid time (on wall clock).
3061 int32_t zoneOffset
= computeZoneOffset(millis
, millisInDay
, status
);
3062 UDate tmpTime
= millis
+ millisInDay
- zoneOffset
;
3065 fZone
->getOffset(tmpTime
, FALSE
, raw
, dst
, status
);
3067 if (U_SUCCESS(status
)) {
3068 // zoneOffset != (raw + dst) only when the given wall time fall into
3069 // a skipped wall time range caused by positive zone offset transition.
3070 if (zoneOffset
!= (raw
+ dst
)) {
3072 status
= U_ILLEGAL_ARGUMENT_ERROR
;
3074 U_ASSERT(fSkippedWallTime
== UCAL_WALLTIME_NEXT_VALID
);
3075 // Adjust time to the next valid wall clock time.
3076 // At this point, tmpTime is on or after the zone offset transition causing
3077 // the skipped time range.
3078 UDate immediatePrevTransition
;
3079 UBool hasTransition
= getImmediatePreviousZoneTransition(tmpTime
, &immediatePrevTransition
, status
);
3080 if (U_SUCCESS(status
) && hasTransition
) {
3081 t
= immediatePrevTransition
;
3089 t
= millis
+ millisInDay
- computeZoneOffset(millis
, millisInDay
, status
);
3092 if (U_SUCCESS(status
)) {
3098 * Find the previous zone transtion near the given time.
3100 UBool
Calendar::getImmediatePreviousZoneTransition(UDate base
, UDate
*transitionTime
, UErrorCode
& status
) const {
3101 BasicTimeZone
*btz
= getBasicTimeZone();
3103 TimeZoneTransition trans
;
3104 UBool hasTransition
= btz
->getPreviousTransition(base
, TRUE
, trans
);
3105 if (hasTransition
) {
3106 *transitionTime
= trans
.getTime();
3109 // Could not find any transitions.
3110 // Note: This should never happen.
3111 status
= U_INTERNAL_PROGRAM_ERROR
;
3114 // If not BasicTimeZone, return unsupported error for now.
3115 // TODO: We may support non-BasicTimeZone in future.
3116 status
= U_UNSUPPORTED_ERROR
;
3122 * Compute the milliseconds in the day from the fields. This is a
3123 * value from 0 to 23:59:59.999 inclusive, unless fields are out of
3124 * range, in which case it can be an arbitrary value. This value
3125 * reflects local zone wall time.
3128 int32_t Calendar::computeMillisInDay() {
3129 // Do the time portion of the conversion.
3131 int32_t millisInDay
= 0;
3133 // Find the best set of fields specifying the time of day. There
3134 // are only two possibilities here; the HOUR_OF_DAY or the
3135 // AM_PM and the HOUR.
3136 int32_t hourOfDayStamp
= fStamp
[UCAL_HOUR_OF_DAY
];
3137 int32_t hourStamp
= (fStamp
[UCAL_HOUR
] > fStamp
[UCAL_AM_PM
])?fStamp
[UCAL_HOUR
]:fStamp
[UCAL_AM_PM
];
3138 int32_t bestStamp
= (hourStamp
> hourOfDayStamp
) ? hourStamp
: hourOfDayStamp
;
3141 if (bestStamp
!= kUnset
) {
3142 if (bestStamp
== hourOfDayStamp
) {
3143 // Don't normalize here; let overflow bump into the next period.
3144 // This is consistent with how we handle other fields.
3145 millisInDay
+= internalGet(UCAL_HOUR_OF_DAY
);
3147 // Don't normalize here; let overflow bump into the next period.
3148 // This is consistent with how we handle other fields.
3149 millisInDay
+= internalGet(UCAL_HOUR
);
3150 millisInDay
+= 12 * internalGet(UCAL_AM_PM
); // Default works for unset AM_PM
3154 // We use the fact that unset == 0; we start with millisInDay
3157 millisInDay
+= internalGet(UCAL_MINUTE
); // now have minutes
3159 millisInDay
+= internalGet(UCAL_SECOND
); // now have seconds
3160 millisInDay
*= 1000;
3161 millisInDay
+= internalGet(UCAL_MILLISECOND
); // now have millis
3167 * This method can assume EXTENDED_YEAR has been set.
3168 * @param millis milliseconds of the date fields
3169 * @param millisInDay milliseconds of the time fields; may be out
3173 int32_t Calendar::computeZoneOffset(double millis
, int32_t millisInDay
, UErrorCode
&ec
) {
3174 int32_t rawOffset
, dstOffset
;
3175 UDate wall
= millis
+ millisInDay
;
3176 BasicTimeZone
* btz
= getBasicTimeZone();
3178 int duplicatedTimeOpt
= (fRepeatedWallTime
== UCAL_WALLTIME_FIRST
) ? BasicTimeZone::kFormer
: BasicTimeZone::kLatter
;
3179 int nonExistingTimeOpt
= (fSkippedWallTime
== UCAL_WALLTIME_FIRST
) ? BasicTimeZone::kLatter
: BasicTimeZone::kFormer
;
3180 btz
->getOffsetFromLocal(wall
, nonExistingTimeOpt
, duplicatedTimeOpt
, rawOffset
, dstOffset
, ec
);
3182 const TimeZone
& tz
= getTimeZone();
3183 // By default, TimeZone::getOffset behaves UCAL_WALLTIME_LAST for both.
3184 tz
.getOffset(wall
, TRUE
, rawOffset
, dstOffset
, ec
);
3186 UBool sawRecentNegativeShift
= FALSE
;
3187 if (fRepeatedWallTime
== UCAL_WALLTIME_FIRST
) {
3188 // Check if the given wall time falls into repeated time range
3189 UDate tgmt
= wall
- (rawOffset
+ dstOffset
);
3191 // Any negative zone transition within last 6 hours?
3192 // Note: The maximum historic negative zone transition is -3 hours in the tz database.
3193 // 6 hour window would be sufficient for this purpose.
3194 int32_t tmpRaw
, tmpDst
;
3195 tz
.getOffset(tgmt
- 6*60*60*1000, FALSE
, tmpRaw
, tmpDst
, ec
);
3196 int32_t offsetDelta
= (rawOffset
+ dstOffset
) - (tmpRaw
+ tmpDst
);
3198 U_ASSERT(offsetDelta
< -6*60*60*1000);
3199 if (offsetDelta
< 0) {
3200 sawRecentNegativeShift
= TRUE
;
3201 // Negative shift within last 6 hours. When UCAL_WALLTIME_FIRST is used and the given wall time falls
3202 // into the repeated time range, use offsets before the transition.
3203 // Note: If it does not fall into the repeated time range, offsets remain unchanged below.
3204 tz
.getOffset(wall
+ offsetDelta
, TRUE
, rawOffset
, dstOffset
, ec
);
3207 if (!sawRecentNegativeShift
&& fSkippedWallTime
== UCAL_WALLTIME_FIRST
) {
3208 // When skipped wall time option is WALLTIME_FIRST,
3209 // recalculate offsets from the resolved time (non-wall).
3210 // When the given wall time falls into skipped wall time,
3211 // the offsets will be based on the zone offsets AFTER
3212 // the transition (which means, earliest possibe interpretation).
3213 UDate tgmt
= wall
- (rawOffset
+ dstOffset
);
3214 tz
.getOffset(tgmt
, FALSE
, rawOffset
, dstOffset
, ec
);
3217 return rawOffset
+ dstOffset
;
3220 int32_t Calendar::computeJulianDay()
3222 // We want to see if any of the date fields is newer than the
3223 // JULIAN_DAY. If not, then we use JULIAN_DAY. If so, then we do
3224 // the normal resolution. We only use JULIAN_DAY if it has been
3225 // set by the user. This makes it possible for the caller to set
3226 // the calendar to a time and call clear(MONTH) to reset the MONTH
3227 // to January. This is legacy behavior. Without this,
3228 // clear(MONTH) has no effect, since the internally set JULIAN_DAY
3230 if (fStamp
[UCAL_JULIAN_DAY
] >= (int32_t)kMinimumUserStamp
) {
3231 int32_t bestStamp
= newestStamp(UCAL_ERA
, UCAL_DAY_OF_WEEK_IN_MONTH
, kUnset
);
3232 bestStamp
= newestStamp(UCAL_YEAR_WOY
, UCAL_EXTENDED_YEAR
, bestStamp
);
3233 if (bestStamp
<= fStamp
[UCAL_JULIAN_DAY
]) {
3234 return internalGet(UCAL_JULIAN_DAY
);
3238 UCalendarDateFields bestField
= resolveFields(getFieldResolutionTable());
3239 if (bestField
== UCAL_FIELD_COUNT
) {
3240 bestField
= UCAL_DAY_OF_MONTH
;
3243 return handleComputeJulianDay(bestField
);
3246 // -------------------------------------------
3248 int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField
) {
3249 UBool useMonth
= (bestField
== UCAL_DAY_OF_MONTH
||
3250 bestField
== UCAL_WEEK_OF_MONTH
||
3251 bestField
== UCAL_DAY_OF_WEEK_IN_MONTH
);
3254 if (bestField
== UCAL_WEEK_OF_YEAR
) {
3255 year
= internalGet(UCAL_YEAR_WOY
, handleGetExtendedYear());
3256 internalSet(UCAL_EXTENDED_YEAR
, year
);
3258 year
= handleGetExtendedYear();
3259 internalSet(UCAL_EXTENDED_YEAR
, year
);
3262 #if defined (U_DEBUG_CAL)
3263 fprintf(stderr
, "%s:%d: bestField= %s - y=%d\n", __FILE__
, __LINE__
, fldName(bestField
), year
);
3266 // Get the Julian day of the day BEFORE the start of this year.
3267 // If useMonth is true, get the day before the start of the month.
3269 // give calendar subclass a chance to have a default 'first' month
3272 if(isSet(UCAL_MONTH
)) {
3273 month
= internalGet(UCAL_MONTH
);
3275 month
= getDefaultMonthInYear(year
);
3278 int32_t julianDay
= handleComputeMonthStart(year
, useMonth
? month
: 0, useMonth
);
3280 if (bestField
== UCAL_DAY_OF_MONTH
) {
3282 // give calendar subclass a chance to have a default 'first' dom
3284 if(isSet(UCAL_DAY_OF_MONTH
)) {
3285 dayOfMonth
= internalGet(UCAL_DAY_OF_MONTH
,1);
3287 dayOfMonth
= getDefaultDayInMonth(year
, month
);
3289 return julianDay
+ dayOfMonth
;
3292 if (bestField
== UCAL_DAY_OF_YEAR
) {
3293 return julianDay
+ internalGet(UCAL_DAY_OF_YEAR
);
3296 int32_t firstDayOfWeek
= getFirstDayOfWeek(); // Localized fdw
3298 // At this point julianDay is the 0-based day BEFORE the first day of
3299 // January 1, year 1 of the given calendar. If julianDay == 0, it
3300 // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
3301 // or Gregorian). (or it is before the month we are in, if useMonth is True)
3303 // At this point we need to process the WEEK_OF_MONTH or
3304 // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
3305 // First, perform initial shared computations. These locate the
3306 // first week of the period.
3308 // Get the 0-based localized DOW of day one of the month or year.
3309 // Valid range 0..6.
3310 int32_t first
= julianDayToDayOfWeek(julianDay
+ 1) - firstDayOfWeek
;
3315 int32_t dowLocal
= getLocalDOW();
3317 // Find the first target DOW (dowLocal) in the month or year.
3318 // Actually, it may be just before the first of the month or year.
3319 // It will be an integer from -5..7.
3320 int32_t date
= 1 - first
+ dowLocal
;
3322 if (bestField
== UCAL_DAY_OF_WEEK_IN_MONTH
) {
3323 // Adjust the target DOW to be in the month or year.
3328 // The only trickiness occurs if the day-of-week-in-month is
3330 int32_t dim
= internalGet(UCAL_DAY_OF_WEEK_IN_MONTH
, 1);
3332 date
+= 7*(dim
- 1);
3335 // Move date to the last of this day-of-week in this month,
3336 // then back up as needed. If dim==-1, we don't back up at
3337 // all. If dim==-2, we back up once, etc. Don't back up
3338 // past the first of the given day-of-week in this month.
3339 // Note that we handle -2, -3, etc. correctly, even though
3340 // values < -1 are technically disallowed.
3341 int32_t m
= internalGet(UCAL_MONTH
, UCAL_JANUARY
);
3342 int32_t monthLength
= handleGetMonthLength(year
, m
);
3343 date
+= ((monthLength
- date
) / 7 + dim
+ 1) * 7;
3346 #if defined (U_DEBUG_CAL)
3347 fprintf(stderr
, "%s:%d - bf= %s\n", __FILE__
, __LINE__
, fldName(bestField
));
3350 if(bestField
== UCAL_WEEK_OF_YEAR
) { // ------------------------------------- WOY -------------
3351 if(!isSet(UCAL_YEAR_WOY
) || // YWOY not set at all or
3352 ( (resolveFields(kYearPrecedence
) != UCAL_YEAR_WOY
) // YWOY doesn't have precedence
3353 && (fStamp
[UCAL_YEAR_WOY
]!=kInternallySet
) ) ) // (excluding where all fields are internally set - then YWOY is used)
3355 // need to be sure to stay in 'real' year.
3356 int32_t woy
= internalGet(bestField
);
3358 int32_t nextJulianDay
= handleComputeMonthStart(year
+1, 0, FALSE
); // jd of day before jan 1
3359 int32_t nextFirst
= julianDayToDayOfWeek(nextJulianDay
+ 1) - firstDayOfWeek
;
3361 if (nextFirst
< 0) { // 0..6 ldow of Jan 1
3365 if(woy
==1) { // FIRST WEEK ---------------------------------
3366 #if defined (U_DEBUG_CAL)
3367 fprintf(stderr
, "%s:%d - woy=%d, yp=%d, nj(%d)=%d, nf=%d", __FILE__
, __LINE__
,
3368 internalGet(bestField
), resolveFields(kYearPrecedence
), year
+1,
3369 nextJulianDay
, nextFirst
);
3371 fprintf(stderr
, " next: %d DFW, min=%d \n", (7-nextFirst
), getMinimalDaysInFirstWeek() );
3374 // nextFirst is now the localized DOW of Jan 1 of y-woy+1
3375 if((nextFirst
> 0) && // Jan 1 starts on FDOW
3376 (7-nextFirst
) >= getMinimalDaysInFirstWeek()) // or enough days in the week
3378 // Jan 1 of (yearWoy+1) is in yearWoy+1 - recalculate JD to next year
3379 #if defined (U_DEBUG_CAL)
3380 fprintf(stderr
, "%s:%d - was going to move JD from %d to %d [d%d]\n", __FILE__
, __LINE__
,
3381 julianDay
, nextJulianDay
, (nextJulianDay
-julianDay
));
3383 julianDay
= nextJulianDay
;
3385 // recalculate 'first' [0-based local dow of jan 1]
3386 first
= julianDayToDayOfWeek(julianDay
+ 1) - firstDayOfWeek
;
3390 // recalculate date.
3391 date
= 1 - first
+ dowLocal
;
3393 } else if(woy
>=getLeastMaximum(bestField
)) {
3394 // could be in the last week- find out if this JD would overstep
3395 int32_t testDate
= date
;
3396 if ((7 - first
) < getMinimalDaysInFirstWeek()) {
3400 // Now adjust for the week number.
3401 testDate
+= 7 * (woy
- 1);
3403 #if defined (U_DEBUG_CAL)
3404 fprintf(stderr
, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n",
3405 __FILE__
, __LINE__
, year
, year
-1, testDate
, julianDay
+testDate
, nextJulianDay
);
3407 if(julianDay
+testDate
> nextJulianDay
) { // is it past Dec 31? (nextJulianDay is day BEFORE year+1's Jan 1)
3408 // Fire up the calculating engines.. retry YWOY = (year-1)
3409 julianDay
= handleComputeMonthStart(year
-1, 0, FALSE
); // jd before Jan 1 of previous year
3410 first
= julianDayToDayOfWeek(julianDay
+ 1) - firstDayOfWeek
; // 0 based local dow of first week
3412 if(first
< 0) { // 0..6
3415 date
= 1 - first
+ dowLocal
;
3417 #if defined (U_DEBUG_CAL)
3418 fprintf(stderr
, "%s:%d - date now %d, jd%d, ywoy%d\n",
3419 __FILE__
, __LINE__
, date
, julianDay
, year
-1);
3423 } /* correction needed */
3424 } /* leastmaximum */
3425 } /* resolvefields(year) != year_woy */
3426 } /* bestfield != week_of_year */
3428 // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR)
3429 // Adjust for minimal days in first week
3430 if ((7 - first
) < getMinimalDaysInFirstWeek()) {
3434 // Now adjust for the week number.
3435 date
+= 7 * (internalGet(bestField
) - 1);
3438 return julianDay
+ date
;
3442 Calendar::getDefaultMonthInYear(int32_t /*eyear*/)
3448 Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/)
3454 int32_t Calendar::getLocalDOW()
3456 // Get zero-based localized DOW, valid range 0..6. This is the DOW
3457 // we are looking for.
3458 int32_t dowLocal
= 0;
3459 switch (resolveFields(kDOWPrecedence
)) {
3460 case UCAL_DAY_OF_WEEK
:
3461 dowLocal
= internalGet(UCAL_DAY_OF_WEEK
) - fFirstDayOfWeek
;
3463 case UCAL_DOW_LOCAL
:
3464 dowLocal
= internalGet(UCAL_DOW_LOCAL
) - 1;
3469 dowLocal
= dowLocal
% 7;
3476 int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy
, int32_t woy
)
3478 // We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine
3479 // what year we fall in, so that other code can set it properly.
3480 // (code borrowed from computeWeekFields and handleComputeJulianDay)
3483 // First, we need a reliable DOW.
3484 UCalendarDateFields bestField
= resolveFields(kDatePrecedence
); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields
3487 int32_t dowLocal
= getLocalDOW(); // 0..6
3488 int32_t firstDayOfWeek
= getFirstDayOfWeek(); // Localized fdw
3489 int32_t jan1Start
= handleComputeMonthStart(yearWoy
, 0, FALSE
);
3490 int32_t nextJan1Start
= handleComputeMonthStart(yearWoy
+1, 0, FALSE
); // next year's Jan1 start
3492 // At this point julianDay is the 0-based day BEFORE the first day of
3493 // January 1, year 1 of the given calendar. If julianDay == 0, it
3494 // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
3495 // or Gregorian). (or it is before the month we are in, if useMonth is True)
3497 // At this point we need to process the WEEK_OF_MONTH or
3498 // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
3499 // First, perform initial shared computations. These locate the
3500 // first week of the period.
3502 // Get the 0-based localized DOW of day one of the month or year.
3503 // Valid range 0..6.
3504 int32_t first
= julianDayToDayOfWeek(jan1Start
+ 1) - firstDayOfWeek
;
3509 //// (nextFirst was not used below)
3510 // int32_t nextFirst = julianDayToDayOfWeek(nextJan1Start + 1) - firstDayOfWeek;
3511 // if (nextFirst < 0) {
3515 int32_t minDays
= getMinimalDaysInFirstWeek();
3516 UBool jan1InPrevYear
= FALSE
; // January 1st in the year of WOY is the 1st week? (i.e. first week is < minimal )
3517 //UBool nextJan1InPrevYear = FALSE; // January 1st of Year of WOY + 1 is in the first week?
3519 if((7 - first
) < minDays
) {
3520 jan1InPrevYear
= TRUE
;
3523 // if((7 - nextFirst) < minDays) {
3524 // nextJan1InPrevYear = TRUE;
3528 case UCAL_WEEK_OF_YEAR
:
3530 if(jan1InPrevYear
== TRUE
) {
3531 // the first week of January is in the previous year
3532 // therefore WOY1 is always solidly within yearWoy
3535 // First WOY is split between two years
3536 if( dowLocal
< first
) { // we are prior to Jan 1
3537 return yearWoy
-1; // previous year
3539 return yearWoy
; // in this year
3542 } else if(woy
>= getLeastMaximum(bestField
)) {
3543 // we _might_ be in the last week..
3544 int32_t jd
= // Calculate JD of our target day:
3545 jan1Start
+ // JD of Jan 1
3546 (7-first
) + // days in the first week (Jan 1.. )
3547 (woy
-1)*7 + // add the weeks of the year
3548 dowLocal
; // the local dow (0..6) of last week
3549 if(jan1InPrevYear
==FALSE
) {
3550 jd
-= 7; // woy already includes Jan 1's week.
3553 if( (jd
+1) >= nextJan1Start
) {
3554 // we are in week 52 or 53 etc. - actual year is yearWoy+1
3557 // still in yearWoy;
3561 // we're not possibly in the last week -must be ywoy
3566 if((internalGet(UCAL_MONTH
)==0) &&
3567 (woy
>= getLeastMaximum(UCAL_WEEK_OF_YEAR
))) {
3568 return yearWoy
+1; // month 0, late woy = in the next year
3570 //if(nextJan1InPrevYear) {
3571 if(internalGet(UCAL_MONTH
)==0) {
3579 //(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow */ ) {
3580 //within 1st week and in this month..
3584 default: // assume the year is appropriate
3589 int32_t Calendar::handleGetMonthLength(int32_t extendedYear
, int32_t month
) const
3591 return handleComputeMonthStart(extendedYear
, month
+1, TRUE
) -
3592 handleComputeMonthStart(extendedYear
, month
, TRUE
);
3595 int32_t Calendar::handleGetYearLength(int32_t eyear
) const {
3596 return handleComputeMonthStart(eyear
+1, 0, FALSE
) -
3597 handleComputeMonthStart(eyear
, 0, FALSE
);
3601 Calendar::getActualMaximum(UCalendarDateFields field
, UErrorCode
& status
) const
3607 if(U_FAILURE(status
)) return 0;
3608 Calendar
*cal
= clone();
3609 if(!cal
) { status
= U_MEMORY_ALLOCATION_ERROR
; return 0; }
3610 cal
->setLenient(TRUE
);
3611 cal
->prepareGetActual(field
,FALSE
,status
);
3612 result
= handleGetMonthLength(cal
->get(UCAL_EXTENDED_YEAR
, status
), cal
->get(UCAL_MONTH
, status
));
3617 case UCAL_DAY_OF_YEAR
:
3619 if(U_FAILURE(status
)) return 0;
3620 Calendar
*cal
= clone();
3621 if(!cal
) { status
= U_MEMORY_ALLOCATION_ERROR
; return 0; }
3622 cal
->setLenient(TRUE
);
3623 cal
->prepareGetActual(field
,FALSE
,status
);
3624 result
= handleGetYearLength(cal
->get(UCAL_EXTENDED_YEAR
, status
));
3629 case UCAL_DAY_OF_WEEK
:
3632 case UCAL_HOUR_OF_DAY
:
3635 case UCAL_MILLISECOND
:
3636 case UCAL_ZONE_OFFSET
:
3637 case UCAL_DST_OFFSET
:
3638 case UCAL_DOW_LOCAL
:
3639 case UCAL_JULIAN_DAY
:
3640 case UCAL_MILLISECONDS_IN_DAY
:
3641 // These fields all have fixed minima/maxima
3642 result
= getMaximum(field
);
3646 // For all other fields, do it the hard way....
3647 result
= getActualHelper(field
, getLeastMaximum(field
), getMaximum(field
),status
);
3655 * Prepare this calendar for computing the actual minimum or maximum.
3656 * This method modifies this calendar's fields; it is called on a
3657 * temporary calendar.
3659 * <p>Rationale: The semantics of getActualXxx() is to return the
3660 * maximum or minimum value that the given field can take, taking into
3661 * account other relevant fields. In general these other fields are
3662 * larger fields. For example, when computing the actual maximum
3663 * DATE, the current value of DATE itself is ignored,
3664 * as is the value of any field smaller.
3666 * <p>The time fields all have fixed minima and maxima, so we don't
3667 * need to worry about them. This also lets us set the
3668 * MILLISECONDS_IN_DAY to zero to erase any effects the time fields
3669 * might have when computing date fields.
3671 * <p>DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and
3672 * WEEK_OF_YEAR fields to ensure that they are computed correctly.
3675 void Calendar::prepareGetActual(UCalendarDateFields field
, UBool isMinimum
, UErrorCode
&status
)
3677 set(UCAL_MILLISECONDS_IN_DAY
, 0);
3681 case UCAL_EXTENDED_YEAR
:
3682 set(UCAL_DAY_OF_YEAR
, getGreatestMinimum(UCAL_DAY_OF_YEAR
));
3686 set(UCAL_WEEK_OF_YEAR
, getGreatestMinimum(UCAL_WEEK_OF_YEAR
));
3689 set(UCAL_DATE
, getGreatestMinimum(UCAL_DATE
));
3692 case UCAL_DAY_OF_WEEK_IN_MONTH
:
3693 // For dowim, the maximum occurs for the DOW of the first of the
3696 set(UCAL_DAY_OF_WEEK
, get(UCAL_DAY_OF_WEEK
, status
)); // Make this user set
3699 case UCAL_WEEK_OF_MONTH
:
3700 case UCAL_WEEK_OF_YEAR
:
3701 // If we're counting weeks, set the day of the week to either the
3702 // first or last localized DOW. We know the last week of a month
3703 // or year will contain the first day of the week, and that the
3704 // first week will contain the last DOW.
3706 int32_t dow
= fFirstDayOfWeek
;
3708 dow
= (dow
+ 6) % 7; // set to last DOW
3709 if (dow
< UCAL_SUNDAY
) {
3713 #if defined (U_DEBUG_CAL)
3714 fprintf(stderr
, "prepareGetActualHelper(WOM/WOY) - dow=%d\n", dow
);
3716 set(UCAL_DAY_OF_WEEK
, dow
);
3723 // Do this last to give it the newest time stamp
3724 set(field
, getGreatestMinimum(field
));
3727 int32_t Calendar::getActualHelper(UCalendarDateFields field
, int32_t startValue
, int32_t endValue
, UErrorCode
&status
) const
3729 #if defined (U_DEBUG_CAL)
3730 fprintf(stderr
, "getActualHelper(%d,%d .. %d, %s)\n", field
, startValue
, endValue
, u_errorName(status
));
3732 if (startValue
== endValue
) {
3733 // if we know that the maximum value is always the same, just return it
3737 int32_t delta
= (endValue
> startValue
) ? 1 : -1;
3739 // clone the calendar so we don't mess with the real one, and set it to
3740 // accept anything for the field values
3741 if(U_FAILURE(status
)) return startValue
;
3742 Calendar
*work
= clone();
3743 if(!work
) { status
= U_MEMORY_ALLOCATION_ERROR
; return startValue
; }
3745 // need to resolve time here, otherwise, fields set for actual limit
3746 // may cause conflict with fields previously set (but not yet resolved).
3747 work
->complete(status
);
3749 work
->setLenient(TRUE
);
3750 work
->prepareGetActual(field
, delta
< 0, status
);
3752 // now try each value from the start to the end one by one until
3753 // we get a value that normalizes to another value. The last value that
3754 // normalizes to itself is the actual maximum for the current date
3755 work
->set(field
, startValue
);
3757 // prepareGetActual sets the first day of week in the same week with
3758 // the first day of a month. Unlike WEEK_OF_YEAR, week number for the
3759 // week which contains days from both previous and current month is
3760 // not unique. For example, last several days in the previous month
3761 // is week 5, and the rest of week is week 1.
3762 int32_t result
= startValue
;
3763 if ((work
->get(field
, status
) != startValue
3764 && field
!= UCAL_WEEK_OF_MONTH
&& delta
> 0 ) || U_FAILURE(status
)) {
3765 #if defined (U_DEBUG_CAL)
3766 fprintf(stderr
, "getActualHelper(fld %d) - got %d (not %d) - %s\n", field
, work
->get(field
,status
), startValue
, u_errorName(status
));
3770 startValue
+= delta
;
3771 work
->add(field
, delta
, status
);
3772 if (work
->get(field
, status
) != startValue
|| U_FAILURE(status
)) {
3773 #if defined (U_DEBUG_CAL)
3774 fprintf(stderr
, "getActualHelper(fld %d) - got %d (not %d), BREAK - %s\n", field
, work
->get(field
,status
), startValue
, u_errorName(status
));
3778 result
= startValue
;
3779 } while (startValue
!= endValue
);
3782 #if defined (U_DEBUG_CAL)
3783 fprintf(stderr
, "getActualHelper(%d) = %d\n", field
, result
);
3791 // -------------------------------------
3794 Calendar::setWeekData(const Locale
& desiredLocale
, const char *type
, UErrorCode
& status
)
3797 if (U_FAILURE(status
)) return;
3799 fFirstDayOfWeek
= UCAL_SUNDAY
;
3800 fMinimalDaysInFirstWeek
= 1;
3801 fWeekendOnset
= UCAL_SATURDAY
;
3802 fWeekendOnsetMillis
= 0;
3803 fWeekendCease
= UCAL_SUNDAY
;
3804 fWeekendCeaseMillis
= 86400000; // 24*60*60*1000
3806 // Since week and weekend data is territory based instead of language based,
3807 // we may need to tweak the locale that we are using to try to get the appropriate
3808 // values, using the following logic:
3809 // 1). If the locale has a language but no territory, use the territory as defined by
3810 // the likely subtags.
3811 // 2). If the locale has a script designation then we ignore it,
3812 // then remove it ( i.e. "en_Latn_US" becomes "en_US" )
3814 char minLocaleID
[ULOC_FULLNAME_CAPACITY
] = { 0 };
3815 UErrorCode myStatus
= U_ZERO_ERROR
;
3817 uloc_minimizeSubtags(desiredLocale
.getName(),minLocaleID
,ULOC_FULLNAME_CAPACITY
,&myStatus
);
3818 Locale min
= Locale::createFromName(minLocaleID
);
3820 if ( uprv_strlen(desiredLocale
.getCountry()) == 0 ||
3821 (uprv_strlen(desiredLocale
.getScript()) > 0 && uprv_strlen(min
.getScript()) == 0) ) {
3822 char maxLocaleID
[ULOC_FULLNAME_CAPACITY
] = { 0 };
3823 myStatus
= U_ZERO_ERROR
;
3824 uloc_addLikelySubtags(desiredLocale
.getName(),maxLocaleID
,ULOC_FULLNAME_CAPACITY
,&myStatus
);
3825 Locale max
= Locale::createFromName(maxLocaleID
);
3826 useLocale
= Locale(max
.getLanguage(),max
.getCountry());
3828 useLocale
= Locale(desiredLocale
);
3831 /* The code here is somewhat of a hack, since week data and weekend data aren't really tied to
3832 a specific calendar, they aren't truly locale data. But this is the only place where valid and
3833 actual locale can be set, so we take a shot at it here by loading a representative resource
3834 from the calendar data. The code used to use the dateTimeElements resource to get first day
3835 of week data, but this was moved to supplemental data under ticket 7755. (JCE) */
3837 CalendarData
calData(useLocale
,type
,status
);
3838 UResourceBundle
*monthNames
= calData
.getByKey(gMonthNames
,status
);
3839 if (U_SUCCESS(status
)) {
3840 U_LOCALE_BASED(locBased
,*this);
3841 locBased
.setLocaleIDs(ures_getLocaleByType(monthNames
, ULOC_VALID_LOCALE
, &status
),
3842 ures_getLocaleByType(monthNames
, ULOC_ACTUAL_LOCALE
, &status
));
3844 status
= U_USING_FALLBACK_WARNING
;
3848 char region
[ULOC_COUNTRY_CAPACITY
];
3849 (void)ulocimp_getRegionForSupplementalData(desiredLocale
.getName(), TRUE
, region
, sizeof(region
), &status
);
3851 // Read week data values from supplementalData week data
3852 UResourceBundle
*rb
= ures_openDirect(NULL
, "supplementalData", &status
);
3853 ures_getByKey(rb
, "weekData", rb
, &status
);
3854 UResourceBundle
*weekData
= ures_getByKey(rb
, region
, NULL
, &status
);
3855 if (status
== U_MISSING_RESOURCE_ERROR
&& rb
!= NULL
) {
3856 status
= U_ZERO_ERROR
;
3857 weekData
= ures_getByKey(rb
, "001", NULL
, &status
);
3860 if (U_FAILURE(status
)) {
3861 #if defined (U_DEBUG_CALDATA)
3862 fprintf(stderr
, " Failure loading weekData from supplemental = %s\n", u_errorName(status
));
3864 status
= U_USING_FALLBACK_WARNING
;
3867 const int32_t *weekDataArr
= ures_getIntVector(weekData
,&arrLen
,&status
);
3868 if( U_SUCCESS(status
) && arrLen
== 6
3869 && 1 <= weekDataArr
[0] && weekDataArr
[0] <= 7
3870 && 1 <= weekDataArr
[1] && weekDataArr
[1] <= 7
3871 && 1 <= weekDataArr
[2] && weekDataArr
[2] <= 7
3872 && 1 <= weekDataArr
[4] && weekDataArr
[4] <= 7) {
3873 fFirstDayOfWeek
= (UCalendarDaysOfWeek
)weekDataArr
[0];
3874 fMinimalDaysInFirstWeek
= (uint8_t)weekDataArr
[1];
3875 fWeekendOnset
= (UCalendarDaysOfWeek
)weekDataArr
[2];
3876 fWeekendOnsetMillis
= weekDataArr
[3];
3877 fWeekendCease
= (UCalendarDaysOfWeek
)weekDataArr
[4];
3878 fWeekendCeaseMillis
= weekDataArr
[5];
3880 status
= U_INVALID_FORMAT_ERROR
;
3883 ures_close(weekData
);
3888 * Recompute the time and update the status fields isTimeSet
3889 * and areFieldsSet. Callers should check isTimeSet and only
3890 * call this method if isTimeSet is false.
3893 Calendar::updateTime(UErrorCode
& status
)
3895 computeTime(status
);
3896 if(U_FAILURE(status
))
3899 // If we are lenient, we need to recompute the fields to normalize
3900 // the values. Also, if we haven't set all the fields yet (i.e.,
3901 // in a newly-created object), we need to fill in the fields. [LIU]
3902 if (isLenient() || ! fAreAllFieldsSet
)
3903 fAreFieldsSet
= FALSE
;
3906 fAreFieldsVirtuallySet
= FALSE
;
3910 Calendar::getLocale(ULocDataLocaleType type
, UErrorCode
& status
) const {
3911 U_LOCALE_BASED(locBased
, *this);
3912 return locBased
.getLocale(type
, status
);
3916 Calendar::getLocaleID(ULocDataLocaleType type
, UErrorCode
& status
) const {
3917 U_LOCALE_BASED(locBased
, *this);
3918 return locBased
.getLocaleID(type
, status
);
3922 Calendar::recalculateStamp() {
3924 int32_t currentValue
;
3929 for (j
= 0; j
< UCAL_FIELD_COUNT
; j
++) {
3930 currentValue
= STAMP_MAX
;
3932 for (i
= 0; i
< UCAL_FIELD_COUNT
; i
++) {
3933 if (fStamp
[i
] > fNextStamp
&& fStamp
[i
] < currentValue
) {
3934 currentValue
= fStamp
[i
];
3940 fStamp
[index
] = ++fNextStamp
;
3948 // Deprecated function. This doesn't need to be inline.
3950 Calendar::internalSet(EDateFields field
, int32_t value
)
3952 internalSet((UCalendarDateFields
) field
, value
);
3956 Calendar::getBasicTimeZone(void) const {
3957 if (dynamic_cast<const OlsonTimeZone
*>(fZone
) != NULL
3958 || dynamic_cast<const SimpleTimeZone
*>(fZone
) != NULL
3959 || dynamic_cast<const RuleBasedTimeZone
*>(fZone
) != NULL
3960 || dynamic_cast<const VTimeZone
*>(fZone
) != NULL
) {
3961 return (BasicTimeZone
*)fZone
;
3968 #endif /* #if !UCONFIG_NO_FORMATTING */