2 *******************************************************************************
3 * Copyright (C) 1997-2011, 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 <typeinfo> // for 'typeid' to work
29 #include "unicode/utypes.h"
31 #if !UCONFIG_NO_FORMATTING
33 #include "unicode/gregocal.h"
41 #include "indiancal.h"
45 #include "unicode/calendar.h"
54 #if !UCONFIG_NO_SERVICE
55 static U_NAMESPACE_QUALIFIER ICULocaleService
* gService
= NULL
;
58 // INTERNAL - for cleanup
61 static UBool
calendar_cleanup(void) {
62 #if !UCONFIG_NO_SERVICE
72 // ------------------------------------------
76 //-------------------------------------------
77 //#define U_DEBUG_CALSVC 1
80 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
83 * fldName was removed as a duplicate implementation.
84 * use udbg_ services instead,
85 * which depend on include files and library from ../tools/ctestfw
87 #include "unicode/udbgutil.h"
91 * convert a UCalendarDateFields into a string - for debugging
93 * @return static string to the field name
97 const char* fldName(UCalendarDateFields f
) {
98 return udbg_enumName(UDBG_UCalendarDateFields
, (int32_t)f
);
102 // from CalendarTest::calToStr - but doesn't modify contents.
103 void ucal_dump(const Calendar
&cal
) {
107 void Calendar::dump() const {
109 fprintf(stderr
, "@calendar=%s, timeset=%c, fieldset=%c, allfields=%c, virtualset=%c, t=%.2f",
110 getType(), fIsTimeSet
?'y':'n', fAreFieldsSet
?'y':'n', fAreAllFieldsSet
?'y':'n',
111 fAreFieldsVirtuallySet
?'y':'n',
114 // can add more things here: DST, zone, etc.
115 fprintf(stderr
, "\n");
116 for(i
= 0;i
<UCAL_FIELD_COUNT
;i
++) {
118 const char *f
= fldName((UCalendarDateFields
)i
);
119 fprintf(stderr
, " %25s: %-11ld", f
, fFields
[i
]);
120 if(fStamp
[i
] == kUnset
) {
121 fprintf(stderr
, " (unset) ");
122 } else if(fStamp
[i
] == kInternallySet
) {
123 fprintf(stderr
, " (internally set) ");
124 //} else if(fStamp[i] == kInternalDefault) {
125 // fprintf(stderr, " (internal default) ");
127 fprintf(stderr
, " %%%d ", fStamp
[i
]);
129 fprintf(stderr
, "\n");
134 U_CFUNC
void ucal_dump(UCalendar
* cal
) {
135 ucal_dump( *((Calendar
*)cal
) );
141 static const char * const gCalTypes
[] = {
154 "ethiopic-amete-alem",
158 // Must be in the order of gCalTypes above
159 typedef enum ECalType
{
160 CALTYPE_UNKNOWN
= -1,
161 CALTYPE_GREGORIAN
= 0,
166 CALTYPE_ISLAMIC_CIVIL
,
173 CALTYPE_ETHIOPIC_AMETE_ALEM
178 static ECalType
getCalendarType(const char *s
) {
179 for (int i
= 0; gCalTypes
[i
] != NULL
; i
++) {
180 if (uprv_stricmp(s
, gCalTypes
[i
]) == 0) {
184 return CALTYPE_UNKNOWN
;
187 static UBool
isStandardSupportedKeyword(const char *keyword
, UErrorCode
& status
) {
188 if(U_FAILURE(status
)) {
191 ECalType calType
= getCalendarType(keyword
);
192 return (calType
!= CALTYPE_UNKNOWN
);
195 static void getCalendarKeyword(const UnicodeString
&id
, char *targetBuffer
, int32_t targetBufferSize
) {
196 UnicodeString calendarKeyword
= UNICODE_STRING_SIMPLE("calendar=");
197 int32_t calKeyLen
= calendarKeyword
.length();
200 int32_t keywordIdx
= id
.indexOf((UChar
)0x003D); /* '=' */
201 if (id
[0] == 0x40/*'@'*/
202 && id
.compareBetween(1, keywordIdx
+1, calendarKeyword
, 0, calKeyLen
) == 0)
204 keyLen
= id
.extract(keywordIdx
+1, id
.length(), targetBuffer
, targetBufferSize
, US_INV
);
206 targetBuffer
[keyLen
] = 0;
209 static ECalType
getCalendarTypeForLocale(const char *locid
) {
210 UErrorCode status
= U_ZERO_ERROR
;
211 ECalType calType
= CALTYPE_UNKNOWN
;
213 //TODO: ULOC_FULL_NAME is out of date and too small..
214 char canonicalName
[256];
216 // canonicalize, so grandfathered variant will be transformed to keywords
217 // e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese
218 int32_t canonicalLen
= uloc_canonicalize(locid
, canonicalName
, sizeof(canonicalName
) - 1, &status
);
219 if (U_FAILURE(status
)) {
220 return CALTYPE_GREGORIAN
;
222 canonicalName
[canonicalLen
] = 0; // terminate
225 int32_t calTypeBufLen
;
227 calTypeBufLen
= uloc_getKeywordValue(canonicalName
, "calendar", calTypeBuf
, sizeof(calTypeBuf
) - 1, &status
);
228 if (U_SUCCESS(status
)) {
229 calTypeBuf
[calTypeBufLen
] = 0;
230 calType
= getCalendarType(calTypeBuf
);
231 if (calType
!= CALTYPE_UNKNOWN
) {
235 status
= U_ZERO_ERROR
;
237 // when calendar keyword is not available or not supported, read supplementalData
238 // to get the default calendar type for the locale's region
239 char region
[ULOC_COUNTRY_CAPACITY
];
240 int32_t regionLen
= 0;
241 regionLen
= uloc_getCountry(canonicalName
, region
, sizeof(region
) - 1, &status
);
242 if (regionLen
== 0) {
244 int32_t fullLocLen
= 0;
245 fullLocLen
= uloc_addLikelySubtags(locid
, fullLoc
, sizeof(fullLoc
) - 1, &status
);
246 regionLen
= uloc_getCountry(fullLoc
, region
, sizeof(region
) - 1, &status
);
248 if (U_FAILURE(status
)) {
249 return CALTYPE_GREGORIAN
;
251 region
[regionLen
] = 0;
253 // Read preferred calendar values from supplementalData calendarPreference
254 UResourceBundle
*rb
= ures_openDirect(NULL
, "supplementalData", &status
);
255 ures_getByKey(rb
, "calendarPreferenceData", rb
, &status
);
256 UResourceBundle
*order
= ures_getByKey(rb
, region
, NULL
, &status
);
257 if (status
== U_MISSING_RESOURCE_ERROR
&& rb
!= NULL
) {
258 status
= U_ZERO_ERROR
;
259 order
= ures_getByKey(rb
, "001", NULL
, &status
);
263 if (U_SUCCESS(status
) && order
!= NULL
) {
264 // the first calender type is the default for the region
266 const UChar
*uCalType
= ures_getStringByIndex(order
, 0, &len
, &status
);
267 if (len
< (int32_t)sizeof(calTypeBuf
)) {
268 u_UCharsToChars(uCalType
, calTypeBuf
, len
);
269 *(calTypeBuf
+ len
) = 0; // terminate;
270 calType
= getCalendarType(calTypeBuf
);
277 if (calType
== CALTYPE_UNKNOWN
) {
279 calType
= CALTYPE_GREGORIAN
;
284 static Calendar
*createStandardCalendar(ECalType calType
, const Locale
&loc
, UErrorCode
& status
) {
285 Calendar
*cal
= NULL
;
288 case CALTYPE_GREGORIAN
:
289 cal
= new GregorianCalendar(loc
, status
);
291 case CALTYPE_JAPANESE
:
292 cal
= new JapaneseCalendar(loc
, status
);
294 case CALTYPE_BUDDHIST
:
295 cal
= new BuddhistCalendar(loc
, status
);
298 cal
= new TaiwanCalendar(loc
, status
);
300 case CALTYPE_PERSIAN
:
301 cal
= new PersianCalendar(loc
, status
);
303 case CALTYPE_ISLAMIC_CIVIL
:
304 cal
= new IslamicCalendar(loc
, status
, IslamicCalendar::CIVIL
);
306 case CALTYPE_ISLAMIC
:
307 cal
= new IslamicCalendar(loc
, status
, IslamicCalendar::ASTRONOMICAL
);
310 cal
= new HebrewCalendar(loc
, status
);
312 case CALTYPE_CHINESE
:
313 cal
= new ChineseCalendar(loc
, status
);
316 cal
= new IndianCalendar(loc
, status
);
319 cal
= new CopticCalendar(loc
, status
);
321 case CALTYPE_ETHIOPIC
:
322 cal
= new EthiopicCalendar(loc
, status
, EthiopicCalendar::AMETE_MIHRET_ERA
);
324 case CALTYPE_ETHIOPIC_AMETE_ALEM
:
325 cal
= new EthiopicCalendar(loc
, status
, EthiopicCalendar::AMETE_ALEM_ERA
);
328 status
= U_UNSUPPORTED_ERROR
;
334 #if !UCONFIG_NO_SERVICE
336 // -------------------------------------
339 * a Calendar Factory which creates the "basic" calendar types, that is, those
342 class BasicCalendarFactory
: public LocaleKeyFactory
{
345 * @param calendarType static const string (caller owns storage - will be aliased) to calendar type
347 BasicCalendarFactory()
348 : LocaleKeyFactory(LocaleKeyFactory::INVISIBLE
) { }
350 virtual ~BasicCalendarFactory() {}
353 //virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const {
354 // if(U_FAILURE(status)) {
357 // char keyword[ULOC_FULLNAME_CAPACITY];
358 // getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword));
359 // return isStandardSupportedKeyword(keyword, status);
362 virtual void updateVisibleIDs(Hashtable
& result
, UErrorCode
& status
) const
364 if (U_SUCCESS(status
)) {
365 for(int32_t i
=0;gCalTypes
[i
] != NULL
;i
++) {
366 UnicodeString
id((UChar
)0x40); /* '@' a variant character */
367 id
.append(UNICODE_STRING_SIMPLE("calendar="));
368 id
.append(UnicodeString(gCalTypes
[i
], -1, US_INV
));
369 result
.put(id
, (void*)this, status
);
374 virtual UObject
* create(const ICUServiceKey
& key
, const ICUService
* /*service*/, UErrorCode
& status
) const {
375 #ifdef U_DEBUG_CALSVC
376 if(dynamic_cast<const LocaleKey
*>(&key
) == NULL
) {
377 fprintf(stderr
, "::create - not a LocaleKey!\n");
380 const LocaleKey
& lkey
= (LocaleKey
&)key
;
381 Locale curLoc
; // current locale
382 Locale canLoc
; // Canonical locale
384 lkey
.currentLocale(curLoc
);
385 lkey
.canonicalLocale(canLoc
);
387 char keyword
[ULOC_FULLNAME_CAPACITY
];
391 getCalendarKeyword(str
, keyword
, (int32_t) sizeof(keyword
));
393 #ifdef U_DEBUG_CALSVC
394 fprintf(stderr
, "BasicCalendarFactory::create() - cur %s, can %s\n", (const char*)curLoc
.getName(), (const char*)canLoc
.getName());
397 if(!isStandardSupportedKeyword(keyword
,status
)) { // Do we handle this type?
398 #ifdef U_DEBUG_CALSVC
400 fprintf(stderr
, "BasicCalendarFactory - not handling %s.[%s]\n", (const char*) curLoc
.getName(), tmp
);
405 return createStandardCalendar(getCalendarType(keyword
), canLoc
, status
);
411 * A factory which looks up the DefaultCalendar resource to determine which class of calendar to use
414 class DefaultCalendarFactory
: public ICUResourceBundleFactory
{
416 DefaultCalendarFactory(): ICUResourceBundleFactory() { }
418 virtual UObject
* create(const ICUServiceKey
& key
, const ICUService
* /*service*/, UErrorCode
& status
) const {
420 LocaleKey
&lkey
= (LocaleKey
&)key
;
422 lkey
.currentLocale(loc
);
424 UnicodeString
*ret
= new UnicodeString();
426 status
= U_MEMORY_ALLOCATION_ERROR
;
428 ret
->append((UChar
)0x40); // '@' is a variant character
429 ret
->append(UNICODE_STRING("calendar=", 9));
430 ret
->append(UnicodeString(gCalTypes
[getCalendarTypeForLocale(loc
.getName())]));
436 // -------------------------------------
437 class CalendarService
: public ICULocaleService
{
440 : ICULocaleService(UNICODE_STRING_SIMPLE("Calendar"))
442 UErrorCode status
= U_ZERO_ERROR
;
443 registerFactory(new DefaultCalendarFactory(), status
);
446 virtual UObject
* cloneInstance(UObject
* instance
) const {
447 UnicodeString
*s
= dynamic_cast<UnicodeString
*>(instance
);
451 #ifdef U_DEBUG_CALSVC_F
452 UErrorCode status2
= U_ZERO_ERROR
;
453 fprintf(stderr
, "Cloning a %s calendar with tz=%ld\n", ((Calendar
*)instance
)->getType(), ((Calendar
*)instance
)->get(UCAL_ZONE_OFFSET
, status2
));
455 return ((Calendar
*)instance
)->clone();
459 virtual UObject
* handleDefault(const ICUServiceKey
& key
, UnicodeString
* /*actualID*/, UErrorCode
& status
) const {
460 LocaleKey
& lkey
= (LocaleKey
&)key
;
461 //int32_t kind = lkey.kind();
464 lkey
.canonicalLocale(loc
);
466 #ifdef U_DEBUG_CALSVC
468 lkey
.currentLocale(loc2
);
469 fprintf(stderr
, "CalSvc:handleDefault for currentLoc %s, canloc %s\n", (const char*)loc
.getName(), (const char*)loc2
.getName());
471 Calendar
*nc
= new GregorianCalendar(loc
, status
);
473 #ifdef U_DEBUG_CALSVC
474 UErrorCode status2
= U_ZERO_ERROR
;
475 fprintf(stderr
, "New default calendar has tz=%d\n", ((Calendar
*)nc
)->get(UCAL_ZONE_OFFSET
, status2
));
480 virtual UBool
isDefault() const {
481 return countFactories() == 1;
485 // -------------------------------------
488 isCalendarServiceUsed() {
490 UMTX_CHECK(NULL
, gService
!= NULL
, retVal
);
494 // -------------------------------------
496 static ICULocaleService
*
497 getCalendarService(UErrorCode
&status
)
500 UMTX_CHECK(NULL
, (UBool
)(gService
== NULL
), needInit
);
502 #ifdef U_DEBUG_CALSVC
503 fprintf(stderr
, "Spinning up Calendar Service\n");
505 ICULocaleService
* newservice
= new CalendarService();
506 if (newservice
== NULL
) {
507 status
= U_MEMORY_ALLOCATION_ERROR
;
510 #ifdef U_DEBUG_CALSVC
511 fprintf(stderr
, "Registering classes..\n");
514 // Register all basic instances.
515 newservice
->registerFactory(new BasicCalendarFactory(),status
);
517 #ifdef U_DEBUG_CALSVC
518 fprintf(stderr
, "Done..\n");
521 if(U_FAILURE(status
)) {
522 #ifdef U_DEBUG_CALSVC
523 fprintf(stderr
, "err (%s) registering classes, deleting service.....\n", u_errorName(status
));
531 if (gService
== NULL
) {
532 gService
= newservice
;
540 // we won the contention - we can register the cleanup.
541 ucln_i18n_registerCleanup(UCLN_I18N_CALENDAR
, calendar_cleanup
);
547 URegistryKey
Calendar::registerFactory(ICUServiceFactory
* toAdopt
, UErrorCode
& status
)
549 return getCalendarService(status
)->registerFactory(toAdopt
, status
);
552 UBool
Calendar::unregister(URegistryKey key
, UErrorCode
& status
) {
553 return getCalendarService(status
)->unregister(key
, status
);
555 #endif /* UCONFIG_NO_SERVICE */
557 // -------------------------------------
559 static const int32_t kCalendarLimits
[UCAL_FIELD_COUNT
][4] = {
560 // Minimum Greatest min Least max Greatest max
561 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // ERA
562 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR
563 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // MONTH
564 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_YEAR
565 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_MONTH
566 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_MONTH
567 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_YEAR
568 { 1, 1, 7, 7 }, // DAY_OF_WEEK
569 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_WEEK_IN_MONTH
570 { 0, 0, 1, 1 }, // AM_PM
571 { 0, 0, 11, 11 }, // HOUR
572 { 0, 0, 23, 23 }, // HOUR_OF_DAY
573 { 0, 0, 59, 59 }, // MINUTE
574 { 0, 0, 59, 59 }, // SECOND
575 { 0, 0, 999, 999 }, // MILLISECOND
576 {-12*kOneHour
, -12*kOneHour
, 12*kOneHour
, 15*kOneHour
}, // ZONE_OFFSET
577 { 0, 0, 1*kOneHour
, 1*kOneHour
}, // DST_OFFSET
578 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR_WOY
579 { 1, 1, 7, 7 }, // DOW_LOCAL
580 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // EXTENDED_YEAR
581 { -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY
582 { 0, 0, 24*kOneHour
-1, 24*kOneHour
-1 }, // MILLISECONDS_IN_DAY
583 { 0, 0, 1, 1 }, // IS_LEAP_MONTH
586 // Resource bundle tags read by this class
587 static const char gDateTimeElements
[] = "DateTimeElements";
588 static const char gWeekend
[] = "weekend";
590 // Data flow in Calendar
591 // ---------------------
593 // The current time is represented in two ways by Calendar: as UTC
594 // milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local
595 // fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the
596 // millis from the fields, and vice versa. The data needed to do this
597 // conversion is encapsulated by a TimeZone object owned by the Calendar.
598 // The data provided by the TimeZone object may also be overridden if the
599 // user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class
600 // keeps track of what information was most recently set by the caller, and
601 // uses that to compute any other information as needed.
603 // If the user sets the fields using set(), the data flow is as follows.
604 // This is implemented by the Calendar subclass's computeTime() method.
605 // During this process, certain fields may be ignored. The disambiguation
606 // algorithm for resolving which fields to pay attention to is described
609 // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
611 // | Using Calendar-specific algorithm
613 // local standard millis
615 // | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET
617 // UTC millis (in time data member)
619 // If the user sets the UTC millis using setTime(), the data flow is as
620 // follows. This is implemented by the Calendar subclass's computeFields()
623 // UTC millis (in time data member)
625 // | Using TimeZone getOffset()
627 // local standard millis
629 // | Using Calendar-specific algorithm
631 // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
633 // In general, a round trip from fields, through local and UTC millis, and
634 // back out to fields is made when necessary. This is implemented by the
635 // complete() method. Resolving a partial set of fields into a UTC millis
636 // value allows all remaining fields to be generated from that value. If
637 // the Calendar is lenient, the fields are also renormalized to standard
638 // ranges when they are regenerated.
640 // -------------------------------------
642 Calendar::Calendar(UErrorCode
& success
)
645 fAreFieldsSet(FALSE
),
646 fAreAllFieldsSet(FALSE
),
647 fAreFieldsVirtuallySet(FALSE
),
648 fNextStamp((int32_t)kMinimumUserStamp
),
654 fZone
= TimeZone::createDefault();
656 success
= U_MEMORY_ALLOCATION_ERROR
;
658 setWeekData(Locale::getDefault(), NULL
, success
);
661 // -------------------------------------
663 Calendar::Calendar(TimeZone
* zone
, const Locale
& aLocale
, UErrorCode
& success
)
666 fAreFieldsSet(FALSE
),
667 fAreAllFieldsSet(FALSE
),
668 fAreFieldsVirtuallySet(FALSE
),
669 fNextStamp((int32_t)kMinimumUserStamp
),
675 #if defined (U_DEBUG_CAL)
676 fprintf(stderr
, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n",
679 success
= U_ILLEGAL_ARGUMENT_ERROR
;
686 setWeekData(aLocale
, NULL
, success
);
689 // -------------------------------------
691 Calendar::Calendar(const TimeZone
& zone
, const Locale
& aLocale
, UErrorCode
& success
)
694 fAreFieldsSet(FALSE
),
695 fAreAllFieldsSet(FALSE
),
696 fAreFieldsVirtuallySet(FALSE
),
697 fNextStamp((int32_t)kMinimumUserStamp
),
703 fZone
= zone
.clone();
705 success
= U_MEMORY_ALLOCATION_ERROR
;
707 setWeekData(aLocale
, NULL
, success
);
710 // -------------------------------------
712 Calendar::~Calendar()
717 // -------------------------------------
719 Calendar::Calendar(const Calendar
&source
)
726 // -------------------------------------
729 Calendar::operator=(const Calendar
&right
)
731 if (this != &right
) {
732 uprv_arrayCopy(right
.fFields
, fFields
, UCAL_FIELD_COUNT
);
733 uprv_arrayCopy(right
.fIsSet
, fIsSet
, UCAL_FIELD_COUNT
);
734 uprv_arrayCopy(right
.fStamp
, fStamp
, UCAL_FIELD_COUNT
);
736 fIsTimeSet
= right
.fIsTimeSet
;
737 fAreAllFieldsSet
= right
.fAreAllFieldsSet
;
738 fAreFieldsSet
= right
.fAreFieldsSet
;
739 fAreFieldsVirtuallySet
= right
.fAreFieldsVirtuallySet
;
740 fLenient
= right
.fLenient
;
744 if (right
.fZone
!= NULL
) {
745 fZone
= right
.fZone
->clone();
747 fFirstDayOfWeek
= right
.fFirstDayOfWeek
;
748 fMinimalDaysInFirstWeek
= right
.fMinimalDaysInFirstWeek
;
749 fWeekendOnset
= right
.fWeekendOnset
;
750 fWeekendOnsetMillis
= right
.fWeekendOnsetMillis
;
751 fWeekendCease
= right
.fWeekendCease
;
752 fWeekendCeaseMillis
= right
.fWeekendCeaseMillis
;
753 fNextStamp
= right
.fNextStamp
;
759 // -------------------------------------
762 Calendar::createInstance(UErrorCode
& success
)
764 return createInstance(TimeZone::createDefault(), Locale::getDefault(), success
);
767 // -------------------------------------
770 Calendar::createInstance(const TimeZone
& zone
, UErrorCode
& success
)
772 return createInstance(zone
, Locale::getDefault(), success
);
775 // -------------------------------------
778 Calendar::createInstance(const Locale
& aLocale
, UErrorCode
& success
)
780 return createInstance(TimeZone::createDefault(), aLocale
, success
);
783 // ------------------------------------- Adopting
785 // Note: this is the bottleneck that actually calls the service routines.
788 Calendar::createInstance(TimeZone
* zone
, const Locale
& aLocale
, UErrorCode
& success
)
790 if (U_FAILURE(success
)) {
797 #if !UCONFIG_NO_SERVICE
798 if (isCalendarServiceUsed()) {
799 u
= getCalendarService(success
)->get(aLocale
, LocaleKey::KIND_ANY
, &actualLoc
, success
);
804 u
= createStandardCalendar(getCalendarTypeForLocale(aLocale
.getName()), aLocale
, success
);
808 if(U_FAILURE(success
) || !u
) {
810 if(U_SUCCESS(success
)) { // Propagate some kind of err
811 success
= U_INTERNAL_PROGRAM_ERROR
;
816 #if !UCONFIG_NO_SERVICE
817 const UnicodeString
* str
= dynamic_cast<const UnicodeString
*>(u
);
819 // It's a unicode string telling us what type of calendar to load ("gregorian", etc)
820 // Create a Locale over this string
822 LocaleUtility::initLocaleFromName(*str
, l
);
824 #ifdef U_DEBUG_CALSVC
825 fprintf(stderr
, "Calendar::createInstance(%s), looking up [%s]\n", aLocale
.getName(), l
.getName());
832 // Don't overwrite actualLoc, since the actual loc from this call
833 // may be something like "@calendar=gregorian" -- TODO investigate
835 c
= (Calendar
*)getCalendarService(success
)->get(l
, LocaleKey::KIND_ANY
, &actualLoc2
, success
);
837 if(U_FAILURE(success
) || !c
) {
839 if(U_SUCCESS(success
)) {
840 success
= U_INTERNAL_PROGRAM_ERROR
; // Propagate some err
845 str
= dynamic_cast<const UnicodeString
*>(c
);
847 // recursed! Second lookup returned a UnicodeString.
848 // Perhaps DefaultCalendar{} was set to another locale.
849 #ifdef U_DEBUG_CALSVC
851 // Extract a char* out of it..
852 int32_t len
= str
->length();
853 int32_t actLen
= sizeof(tmp
)-1;
857 str
->extract(0,len
,tmp
);
860 fprintf(stderr
, "err - recursed, 2nd lookup was unistring %s\n", tmp
);
862 success
= U_MISSING_RESOURCE_ERROR
; // requested a calendar type which could NOT be found.
867 #ifdef U_DEBUG_CALSVC
868 fprintf(stderr
, "%p: setting week count data to locale %s, actual locale %s\n", c
, (const char*)aLocale
.getName(), (const char *)actualLoc
.getName());
870 c
->setWeekData(aLocale
, c
->getType(), success
); // set the correct locale (this was an indirected calendar)
873 #endif /* UCONFIG_NO_SERVICE */
875 // a calendar was returned - we assume the factory did the right thing.
879 // Now, reset calendar to default state:
880 c
->adoptTimeZone(zone
); // Set the correct time zone
881 c
->setTimeInMillis(getNow(), success
); // let the new calendar have the current time.
886 // -------------------------------------
889 Calendar::createInstance(const TimeZone
& zone
, const Locale
& aLocale
, UErrorCode
& success
)
891 Calendar
* c
= createInstance(aLocale
, success
);
892 if(U_SUCCESS(success
) && c
) {
893 c
->setTimeZone(zone
);
898 // -------------------------------------
901 Calendar::operator==(const Calendar
& that
) const
903 UErrorCode status
= U_ZERO_ERROR
;
904 return isEquivalentTo(that
) &&
905 getTimeInMillis(status
) == that
.getTimeInMillis(status
) &&
910 Calendar::isEquivalentTo(const Calendar
& other
) const
912 return typeid(*this) == typeid(other
) &&
913 fLenient
== other
.fLenient
&&
914 fFirstDayOfWeek
== other
.fFirstDayOfWeek
&&
915 fMinimalDaysInFirstWeek
== other
.fMinimalDaysInFirstWeek
&&
916 fWeekendOnset
== other
.fWeekendOnset
&&
917 fWeekendOnsetMillis
== other
.fWeekendOnsetMillis
&&
918 fWeekendCease
== other
.fWeekendCease
&&
919 fWeekendCeaseMillis
== other
.fWeekendCeaseMillis
&&
920 *fZone
== *other
.fZone
;
923 // -------------------------------------
926 Calendar::equals(const Calendar
& when
, UErrorCode
& status
) const
928 return (this == &when
||
929 getTime(status
) == when
.getTime(status
));
932 // -------------------------------------
935 Calendar::before(const Calendar
& when
, UErrorCode
& status
) const
937 return (this != &when
&&
938 getTimeInMillis(status
) < when
.getTimeInMillis(status
));
941 // -------------------------------------
944 Calendar::after(const Calendar
& when
, UErrorCode
& status
) const
946 return (this != &when
&&
947 getTimeInMillis(status
) > when
.getTimeInMillis(status
));
950 // -------------------------------------
953 const Locale
* U_EXPORT2
954 Calendar::getAvailableLocales(int32_t& count
)
956 return Locale::getAvailableLocales(count
);
959 // -------------------------------------
961 StringEnumeration
* U_EXPORT2
962 Calendar::getKeywordValuesForLocale(const char* key
,
963 const Locale
& locale
, UBool commonlyUsed
, UErrorCode
& status
)
965 // This is a wrapper over ucal_getKeywordValuesForLocale
966 UEnumeration
*uenum
= ucal_getKeywordValuesForLocale(key
, locale
.getName(),
967 commonlyUsed
, &status
);
968 if (U_FAILURE(status
)) {
972 return new UStringEnumeration(uenum
);
975 // -------------------------------------
980 return uprv_getUTCtime(); // return as milliseconds
983 // -------------------------------------
986 * Gets this Calendar's current time as a long.
987 * @return the current time as UTC milliseconds from the epoch.
990 Calendar::getTimeInMillis(UErrorCode
& status
) const
992 if(U_FAILURE(status
))
996 ((Calendar
*)this)->updateTime(status
);
998 /* Test for buffer overflows */
999 if(U_FAILURE(status
)) {
1005 // -------------------------------------
1008 * Sets this Calendar's current time from the given long value.
1009 * @param date the new time in UTC milliseconds from the epoch.
1012 Calendar::setTimeInMillis( double millis
, UErrorCode
& status
) {
1013 if(U_FAILURE(status
))
1016 if (millis
> MAX_MILLIS
) {
1017 millis
= MAX_MILLIS
;
1018 } else if (millis
< MIN_MILLIS
) {
1019 millis
= MIN_MILLIS
;
1023 fAreFieldsSet
= fAreAllFieldsSet
= FALSE
;
1024 fIsTimeSet
= fAreFieldsVirtuallySet
= TRUE
;
1026 for (int32_t i
=0; i
<UCAL_FIELD_COUNT
; ++i
) {
1035 // -------------------------------------
1038 Calendar::get(UCalendarDateFields field
, UErrorCode
& status
) const
1040 // field values are only computed when actually requested; for more on when computation
1041 // of various things happens, see the "data flow in Calendar" description at the top
1043 if (U_SUCCESS(status
)) ((Calendar
*)this)->complete(status
); // Cast away const
1044 return U_SUCCESS(status
) ? fFields
[field
] : 0;
1047 // -------------------------------------
1050 Calendar::set(UCalendarDateFields field
, int32_t value
)
1052 if (fAreFieldsVirtuallySet
) {
1053 UErrorCode ec
= U_ZERO_ERROR
;
1056 fFields
[field
] = value
;
1057 fStamp
[field
] = fNextStamp
++;
1058 fIsSet
[field
] = TRUE
; // Remove later
1059 fIsTimeSet
= fAreFieldsSet
= fAreFieldsVirtuallySet
= FALSE
;
1062 // -------------------------------------
1065 Calendar::set(int32_t year
, int32_t month
, int32_t date
)
1067 set(UCAL_YEAR
, year
);
1068 set(UCAL_MONTH
, month
);
1069 set(UCAL_DATE
, date
);
1072 // -------------------------------------
1075 Calendar::set(int32_t year
, int32_t month
, int32_t date
, int32_t hour
, int32_t minute
)
1077 set(UCAL_YEAR
, year
);
1078 set(UCAL_MONTH
, month
);
1079 set(UCAL_DATE
, date
);
1080 set(UCAL_HOUR_OF_DAY
, hour
);
1081 set(UCAL_MINUTE
, minute
);
1084 // -------------------------------------
1087 Calendar::set(int32_t year
, int32_t month
, int32_t date
, int32_t hour
, int32_t minute
, int32_t second
)
1089 set(UCAL_YEAR
, year
);
1090 set(UCAL_MONTH
, month
);
1091 set(UCAL_DATE
, date
);
1092 set(UCAL_HOUR_OF_DAY
, hour
);
1093 set(UCAL_MINUTE
, minute
);
1094 set(UCAL_SECOND
, second
);
1097 // -------------------------------------
1102 for (int32_t i
=0; i
<UCAL_FIELD_COUNT
; ++i
) {
1103 fFields
[i
] = 0; // Must do this; other code depends on it
1105 fIsSet
[i
] = FALSE
; // Remove later
1107 fIsTimeSet
= fAreFieldsSet
= fAreAllFieldsSet
= fAreFieldsVirtuallySet
= FALSE
;
1108 // fTime is not 'cleared' - may be used if no fields are set.
1111 // -------------------------------------
1114 Calendar::clear(UCalendarDateFields field
)
1116 if (fAreFieldsVirtuallySet
) {
1117 UErrorCode ec
= U_ZERO_ERROR
;
1121 fStamp
[field
] = kUnset
;
1122 fIsSet
[field
] = FALSE
; // Remove later
1123 fIsTimeSet
= fAreFieldsSet
= fAreAllFieldsSet
= fAreFieldsVirtuallySet
= FALSE
;
1126 // -------------------------------------
1129 Calendar::isSet(UCalendarDateFields field
) const
1131 return fAreFieldsVirtuallySet
|| (fStamp
[field
] != kUnset
);
1135 int32_t Calendar::newestStamp(UCalendarDateFields first
, UCalendarDateFields last
, int32_t bestStampSoFar
) const
1137 int32_t bestStamp
= bestStampSoFar
;
1138 for (int32_t i
=(int32_t)first
; i
<=(int32_t)last
; ++i
) {
1139 if (fStamp
[i
] > bestStamp
) {
1140 bestStamp
= fStamp
[i
];
1147 // -------------------------------------
1150 Calendar::complete(UErrorCode
& status
)
1154 /* Test for buffer overflows */
1155 if(U_FAILURE(status
)) {
1159 if (!fAreFieldsSet
) {
1160 computeFields(status
); // fills in unset fields
1161 /* Test for buffer overflows */
1162 if(U_FAILURE(status
)) {
1165 fAreFieldsSet
= TRUE
;
1166 fAreAllFieldsSet
= TRUE
;
1170 //-------------------------------------------------------------------------
1171 // Protected utility methods for use by subclasses. These are very handy
1172 // for implementing add, roll, and computeFields.
1173 //-------------------------------------------------------------------------
1176 * Adjust the specified field so that it is within
1177 * the allowable range for the date to which this calendar is set.
1178 * For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH}
1179 * field for a calendar set to April 31 would cause it to be set
1182 * <b>Subclassing:</b>
1184 * This utility method is intended for use by subclasses that need to implement
1185 * their own overrides of {@link #roll roll} and {@link #add add}.
1188 * <code>pinField</code> is implemented in terms of
1189 * {@link #getActualMinimum getActualMinimum}
1190 * and {@link #getActualMaximum getActualMaximum}. If either of those methods uses
1191 * a slow, iterative algorithm for a particular field, it would be
1192 * unwise to attempt to call <code>pinField</code> for that field. If you
1193 * really do need to do so, you should override this method to do
1194 * something more efficient for that field.
1196 * @param field The calendar field whose value should be pinned.
1198 * @see #getActualMinimum
1199 * @see #getActualMaximum
1202 void Calendar::pinField(UCalendarDateFields field
, UErrorCode
& status
) {
1203 int32_t max
= getActualMaximum(field
, status
);
1204 int32_t min
= getActualMinimum(field
, status
);
1206 if (fFields
[field
] > max
) {
1208 } else if (fFields
[field
] < min
) {
1214 void Calendar::computeFields(UErrorCode
&ec
)
1216 if (U_FAILURE(ec
)) {
1219 // Compute local wall millis
1220 double localMillis
= internalGetTime();
1221 int32_t rawOffset
, dstOffset
;
1222 getTimeZone().getOffset(localMillis
, FALSE
, rawOffset
, dstOffset
, ec
);
1223 localMillis
+= (rawOffset
+ dstOffset
);
1225 // Mark fields as set. Do this before calling handleComputeFields().
1226 uint32_t mask
= //fInternalSetMask;
1230 (1 << UCAL_DAY_OF_MONTH
) | // = UCAL_DATE
1231 (1 << UCAL_DAY_OF_YEAR
) |
1232 (1 << UCAL_EXTENDED_YEAR
);
1234 for (int32_t i
=0; i
<UCAL_FIELD_COUNT
; ++i
) {
1235 if ((mask
& 1) == 0) {
1236 fStamp
[i
] = kInternallySet
;
1237 fIsSet
[i
] = TRUE
; // Remove later
1240 fIsSet
[i
] = FALSE
; // Remove later
1245 // We used to check for and correct extreme millis values (near
1246 // Long.MIN_VALUE or Long.MAX_VALUE) here. Such values would cause
1247 // overflows from positive to negative (or vice versa) and had to
1248 // be manually tweaked. We no longer need to do this because we
1249 // have limited the range of supported dates to those that have a
1250 // Julian day that fits into an int. This allows us to implement a
1251 // JULIAN_DAY field and also removes some inelegant code. - Liu
1254 int32_t days
= (int32_t)ClockMath::floorDivide(localMillis
, (double)kOneDay
);
1256 internalSet(UCAL_JULIAN_DAY
,days
+ kEpochStartAsJulianDay
);
1258 #if defined (U_DEBUG_CAL)
1259 //fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n",
1260 //__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
1263 computeGregorianAndDOWFields(fFields
[UCAL_JULIAN_DAY
], ec
);
1265 // Call framework method to have subclass compute its fields.
1266 // These must include, at a minimum, MONTH, DAY_OF_MONTH,
1267 // EXTENDED_YEAR, YEAR, DAY_OF_YEAR. This method will call internalSet(),
1268 // which will update stamp[].
1269 handleComputeFields(fFields
[UCAL_JULIAN_DAY
], ec
);
1271 // Compute week-related fields, based on the subclass-computed
1272 // fields computed by handleComputeFields().
1273 computeWeekFields(ec
);
1275 // Compute time-related fields. These are indepent of the date and
1276 // of the subclass algorithm. They depend only on the local zone
1277 // wall milliseconds in day.
1278 int32_t millisInDay
= (int32_t) (localMillis
- (days
* kOneDay
));
1279 fFields
[UCAL_MILLISECONDS_IN_DAY
] = millisInDay
;
1280 fFields
[UCAL_MILLISECOND
] = millisInDay
% 1000;
1281 millisInDay
/= 1000;
1282 fFields
[UCAL_SECOND
] = millisInDay
% 60;
1284 fFields
[UCAL_MINUTE
] = millisInDay
% 60;
1286 fFields
[UCAL_HOUR_OF_DAY
] = millisInDay
;
1287 fFields
[UCAL_AM_PM
] = millisInDay
/ 12; // Assume AM == 0
1288 fFields
[UCAL_HOUR
] = millisInDay
% 12;
1289 fFields
[UCAL_ZONE_OFFSET
] = rawOffset
;
1290 fFields
[UCAL_DST_OFFSET
] = dstOffset
;
1293 uint8_t Calendar::julianDayToDayOfWeek(double julian
)
1295 // If julian is negative, then julian%7 will be negative, so we adjust
1296 // accordingly. We add 1 because Julian day 0 is Monday.
1297 int8_t dayOfWeek
= (int8_t) uprv_fmod(julian
+ 1, 7);
1299 uint8_t result
= (uint8_t)(dayOfWeek
+ ((dayOfWeek
< 0) ? (7+UCAL_SUNDAY
) : UCAL_SUNDAY
));
1304 * Compute the Gregorian calendar year, month, and day of month from
1305 * the given Julian day. These values are not stored in fields, but in
1306 * member variables gregorianXxx. Also compute the DAY_OF_WEEK and
1309 void Calendar::computeGregorianAndDOWFields(int32_t julianDay
, UErrorCode
&ec
)
1311 computeGregorianFields(julianDay
, ec
);
1313 // Compute day of week: JD 0 = Monday
1314 int32_t dow
= julianDayToDayOfWeek(julianDay
);
1315 internalSet(UCAL_DAY_OF_WEEK
,dow
);
1317 // Calculate 1-based localized day of week
1318 int32_t dowLocal
= dow
- getFirstDayOfWeek() + 1;
1322 internalSet(UCAL_DOW_LOCAL
,dowLocal
);
1323 fFields
[UCAL_DOW_LOCAL
] = dowLocal
;
1327 * Compute the Gregorian calendar year, month, and day of month from the
1328 * Julian day. These values are not stored in fields, but in member
1329 * variables gregorianXxx. They are used for time zone computations and by
1330 * subclasses that are Gregorian derivatives. Subclasses may call this
1331 * method to perform a Gregorian calendar millis->fields computation.
1333 void Calendar::computeGregorianFields(int32_t julianDay
, UErrorCode
& /* ec */) {
1334 int32_t gregorianDayOfWeekUnused
;
1335 Grego::dayToFields(julianDay
- kEpochStartAsJulianDay
, fGregorianYear
, fGregorianMonth
, fGregorianDayOfMonth
, gregorianDayOfWeekUnused
, fGregorianDayOfYear
);
1339 * Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH,
1340 * DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR,
1341 * DAY_OF_WEEK, and DAY_OF_YEAR. The latter fields are computed by the
1342 * subclass based on the calendar system.
1344 * <p>The YEAR_WOY field is computed simplistically. It is equal to YEAR
1345 * most of the time, but at the year boundary it may be adjusted to YEAR-1
1346 * or YEAR+1 to reflect the overlap of a week into an adjacent year. In
1347 * this case, a simple increment or decrement is performed on YEAR, even
1348 * though this may yield an invalid YEAR value. For instance, if the YEAR
1349 * is part of a calendar system with an N-year cycle field CYCLE, then
1350 * incrementing the YEAR may involve incrementing CYCLE and setting YEAR
1351 * back to 0 or 1. This is not handled by this code, and in fact cannot be
1352 * simply handled without having subclasses define an entire parallel set of
1353 * fields for fields larger than or equal to a year. This additional
1354 * complexity is not warranted, since the intention of the YEAR_WOY field is
1355 * to support ISO 8601 notation, so it will typically be used with a
1356 * proleptic Gregorian calendar, which has no field larger than a year.
1358 void Calendar::computeWeekFields(UErrorCode
&ec
) {
1362 int32_t eyear
= fFields
[UCAL_EXTENDED_YEAR
];
1363 int32_t dayOfWeek
= fFields
[UCAL_DAY_OF_WEEK
];
1364 int32_t dayOfYear
= fFields
[UCAL_DAY_OF_YEAR
];
1366 // WEEK_OF_YEAR start
1367 // Compute the week of the year. For the Gregorian calendar, valid week
1368 // numbers run from 1 to 52 or 53, depending on the year, the first day
1369 // of the week, and the minimal days in the first week. For other
1370 // calendars, the valid range may be different -- it depends on the year
1371 // length. Days at the start of the year may fall into the last week of
1372 // the previous year; days at the end of the year may fall into the
1373 // first week of the next year. ASSUME that the year length is less than
1375 int32_t yearOfWeekOfYear
= eyear
;
1376 int32_t relDow
= (dayOfWeek
+ 7 - getFirstDayOfWeek()) % 7; // 0..6
1377 int32_t relDowJan1
= (dayOfWeek
- dayOfYear
+ 7001 - getFirstDayOfWeek()) % 7; // 0..6
1378 int32_t woy
= (dayOfYear
- 1 + relDowJan1
) / 7; // 0..53
1379 if ((7 - relDowJan1
) >= getMinimalDaysInFirstWeek()) {
1383 // Adjust for weeks at the year end that overlap into the previous or
1384 // next calendar year.
1386 // We are the last week of the previous year.
1387 // Check to see if we are in the last week; if so, we need
1388 // to handle the case in which we are the first week of the
1391 int32_t prevDoy
= dayOfYear
+ handleGetYearLength(eyear
- 1);
1392 woy
= weekNumber(prevDoy
, dayOfWeek
);
1395 int32_t lastDoy
= handleGetYearLength(eyear
);
1396 // Fast check: For it to be week 1 of the next year, the DOY
1397 // must be on or after L-5, where L is yearLength(), then it
1398 // cannot possibly be week 1 of the next year:
1400 // doy: 359 360 361 362 363 364 365 001
1401 // dow: 1 2 3 4 5 6 7
1402 if (dayOfYear
>= (lastDoy
- 5)) {
1403 int32_t lastRelDow
= (relDow
+ lastDoy
- dayOfYear
) % 7;
1404 if (lastRelDow
< 0) {
1407 if (((6 - lastRelDow
) >= getMinimalDaysInFirstWeek()) &&
1408 ((dayOfYear
+ 7 - relDow
) > lastDoy
)) {
1414 fFields
[UCAL_WEEK_OF_YEAR
] = woy
;
1415 fFields
[UCAL_YEAR_WOY
] = yearOfWeekOfYear
;
1418 int32_t dayOfMonth
= fFields
[UCAL_DAY_OF_MONTH
];
1419 fFields
[UCAL_WEEK_OF_MONTH
] = weekNumber(dayOfMonth
, dayOfWeek
);
1420 fFields
[UCAL_DAY_OF_WEEK_IN_MONTH
] = (dayOfMonth
-1) / 7 + 1;
1421 #if defined (U_DEBUG_CAL)
1422 if(fFields
[UCAL_DAY_OF_WEEK_IN_MONTH
]==0) fprintf(stderr
, "%s:%d: DOWIM %d on %g\n",
1423 __FILE__
, __LINE__
,fFields
[UCAL_DAY_OF_WEEK_IN_MONTH
], fTime
);
1428 int32_t Calendar::weekNumber(int32_t desiredDay
, int32_t dayOfPeriod
, int32_t dayOfWeek
)
1430 // Determine the day of the week of the first day of the period
1431 // in question (either a year or a month). Zero represents the
1432 // first day of the week on this calendar.
1433 int32_t periodStartDayOfWeek
= (dayOfWeek
- getFirstDayOfWeek() - dayOfPeriod
+ 1) % 7;
1434 if (periodStartDayOfWeek
< 0) periodStartDayOfWeek
+= 7;
1436 // Compute the week number. Initially, ignore the first week, which
1437 // may be fractional (or may not be). We add periodStartDayOfWeek in
1438 // order to fill out the first week, if it is fractional.
1439 int32_t weekNo
= (desiredDay
+ periodStartDayOfWeek
- 1)/7;
1441 // If the first week is long enough, then count it. If
1442 // the minimal days in the first week is one, or if the period start
1443 // is zero, we always increment weekNo.
1444 if ((7 - periodStartDayOfWeek
) >= getMinimalDaysInFirstWeek()) ++weekNo
;
1449 void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode
&/* status */)
1451 internalSet(UCAL_MONTH
, getGregorianMonth());
1452 internalSet(UCAL_DAY_OF_MONTH
, getGregorianDayOfMonth());
1453 internalSet(UCAL_DAY_OF_YEAR
, getGregorianDayOfYear());
1454 int32_t eyear
= getGregorianYear();
1455 internalSet(UCAL_EXTENDED_YEAR
, eyear
);
1456 int32_t era
= GregorianCalendar::AD
;
1458 era
= GregorianCalendar::BC
;
1461 internalSet(UCAL_ERA
, era
);
1462 internalSet(UCAL_YEAR
, eyear
);
1464 // -------------------------------------
1467 void Calendar::roll(EDateFields field
, int32_t amount
, UErrorCode
& status
)
1469 roll((UCalendarDateFields
)field
, amount
, status
);
1472 void Calendar::roll(UCalendarDateFields field
, int32_t amount
, UErrorCode
& status
)
1475 return; // Nothing to do
1480 if(U_FAILURE(status
)) {
1484 case UCAL_DAY_OF_MONTH
:
1488 case UCAL_MILLISECOND
:
1489 case UCAL_MILLISECONDS_IN_DAY
:
1491 // These are the standard roll instructions. These work for all
1492 // simple cases, that is, cases in which the limits are fixed, such
1493 // as the hour, the day of the month, and the era.
1495 int32_t min
= getActualMinimum(field
,status
);
1496 int32_t max
= getActualMaximum(field
,status
);
1497 int32_t gap
= max
- min
+ 1;
1499 int32_t value
= internalGet(field
) + amount
;
1500 value
= (value
- min
) % gap
;
1511 case UCAL_HOUR_OF_DAY
:
1512 // Rolling the hour is difficult on the ONSET and CEASE days of
1513 // daylight savings. For example, if the change occurs at
1514 // 2 AM, we have the following progression:
1515 // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst
1516 // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std
1517 // To get around this problem we don't use fields; we manipulate
1518 // the time in millis directly.
1520 // Assume min == 0 in calculations below
1521 double start
= getTimeInMillis(status
);
1522 int32_t oldHour
= internalGet(field
);
1523 int32_t max
= getMaximum(field
);
1524 int32_t newHour
= (oldHour
+ amount
) % (max
+ 1);
1528 setTimeInMillis(start
+ kOneHour
* (newHour
- oldHour
),status
);
1533 // Rolling the month involves both pinning the final value
1534 // and adjusting the DAY_OF_MONTH if necessary. We only adjust the
1535 // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
1536 // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
1538 int32_t max
= getActualMaximum(UCAL_MONTH
, status
);
1539 int32_t mon
= (internalGet(UCAL_MONTH
) + amount
) % (max
+1);
1544 set(UCAL_MONTH
, mon
);
1546 // Keep the day of month in range. We don't want to spill over
1547 // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 ->
1549 pinField(UCAL_DAY_OF_MONTH
,status
);
1555 case UCAL_EXTENDED_YEAR
:
1556 // Rolling the year can involve pinning the DAY_OF_MONTH.
1557 set(field
, internalGet(field
) + amount
);
1558 pinField(UCAL_MONTH
,status
);
1559 pinField(UCAL_DAY_OF_MONTH
,status
);
1562 case UCAL_WEEK_OF_MONTH
:
1564 // This is tricky, because during the roll we may have to shift
1565 // to a different day of the week. For example:
1571 // When rolling from the 6th or 7th back one week, we go to the
1572 // 1st (assuming that the first partial week counts). The same
1573 // thing happens at the end of the month.
1575 // The other tricky thing is that we have to figure out whether
1576 // the first partial week actually counts or not, based on the
1577 // minimal first days in the week. And we have to use the
1578 // correct first day of the week to delineate the week
1581 // Here's our algorithm. First, we find the real boundaries of
1582 // the month. Then we discard the first partial week if it
1583 // doesn't count in this locale. Then we fill in the ends with
1584 // phantom days, so that the first partial week and the last
1585 // partial week are full weeks. We then have a nice square
1586 // block of weeks. We do the usual rolling within this block,
1587 // as is done elsewhere in this method. If we wind up on one of
1588 // the phantom days that we added, we recognize this and pin to
1589 // the first or the last day of the month. Easy, eh?
1591 // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
1592 // in this locale. We have dow in 0..6.
1593 int32_t dow
= internalGet(UCAL_DAY_OF_WEEK
) - getFirstDayOfWeek();
1594 if (dow
< 0) dow
+= 7;
1596 // Find the day of the week (normalized for locale) for the first
1598 int32_t fdm
= (dow
- internalGet(UCAL_DAY_OF_MONTH
) + 1) % 7;
1599 if (fdm
< 0) fdm
+= 7;
1601 // Get the first day of the first full week of the month,
1602 // including phantom days, if any. Figure out if the first week
1603 // counts or not; if it counts, then fill in phantom days. If
1604 // not, advance to the first real full week (skip the partial week).
1606 if ((7 - fdm
) < getMinimalDaysInFirstWeek())
1607 start
= 8 - fdm
; // Skip the first partial week
1609 start
= 1 - fdm
; // This may be zero or negative
1611 // Get the day of the week (normalized for locale) for the last
1612 // day of the month.
1613 int32_t monthLen
= getActualMaximum(UCAL_DAY_OF_MONTH
, status
);
1614 int32_t ldm
= (monthLen
- internalGet(UCAL_DAY_OF_MONTH
) + dow
) % 7;
1615 // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.
1617 // Get the limit day for the blocked-off rectangular month; that
1618 // is, the day which is one past the last day of the month,
1619 // after the month has already been filled in with phantom days
1620 // to fill out the last week. This day has a normalized DOW of 0.
1621 int32_t limit
= monthLen
+ 7 - ldm
;
1623 // Now roll between start and (limit - 1).
1624 int32_t gap
= limit
- start
;
1625 int32_t day_of_month
= (internalGet(UCAL_DAY_OF_MONTH
) + amount
*7 -
1627 if (day_of_month
< 0) day_of_month
+= gap
;
1628 day_of_month
+= start
;
1630 // Finally, pin to the real start and end of the month.
1631 if (day_of_month
< 1) day_of_month
= 1;
1632 if (day_of_month
> monthLen
) day_of_month
= monthLen
;
1634 // Set the DAY_OF_MONTH. We rely on the fact that this field
1635 // takes precedence over everything else (since all other fields
1636 // are also set at this point). If this fact changes (if the
1637 // disambiguation algorithm changes) then we will have to unset
1638 // the appropriate fields here so that DAY_OF_MONTH is attended
1640 set(UCAL_DAY_OF_MONTH
, day_of_month
);
1643 case UCAL_WEEK_OF_YEAR
:
1645 // This follows the outline of WEEK_OF_MONTH, except it applies
1646 // to the whole year. Please see the comment for WEEK_OF_MONTH
1647 // for general notes.
1649 // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
1650 // in this locale. We have dow in 0..6.
1651 int32_t dow
= internalGet(UCAL_DAY_OF_WEEK
) - getFirstDayOfWeek();
1652 if (dow
< 0) dow
+= 7;
1654 // Find the day of the week (normalized for locale) for the first
1656 int32_t fdy
= (dow
- internalGet(UCAL_DAY_OF_YEAR
) + 1) % 7;
1657 if (fdy
< 0) fdy
+= 7;
1659 // Get the first day of the first full week of the year,
1660 // including phantom days, if any. Figure out if the first week
1661 // counts or not; if it counts, then fill in phantom days. If
1662 // not, advance to the first real full week (skip the partial week).
1664 if ((7 - fdy
) < getMinimalDaysInFirstWeek())
1665 start
= 8 - fdy
; // Skip the first partial week
1667 start
= 1 - fdy
; // This may be zero or negative
1669 // Get the day of the week (normalized for locale) for the last
1671 int32_t yearLen
= getActualMaximum(UCAL_DAY_OF_YEAR
,status
);
1672 int32_t ldy
= (yearLen
- internalGet(UCAL_DAY_OF_YEAR
) + dow
) % 7;
1673 // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here.
1675 // Get the limit day for the blocked-off rectangular year; that
1676 // is, the day which is one past the last day of the year,
1677 // after the year has already been filled in with phantom days
1678 // to fill out the last week. This day has a normalized DOW of 0.
1679 int32_t limit
= yearLen
+ 7 - ldy
;
1681 // Now roll between start and (limit - 1).
1682 int32_t gap
= limit
- start
;
1683 int32_t day_of_year
= (internalGet(UCAL_DAY_OF_YEAR
) + amount
*7 -
1685 if (day_of_year
< 0) day_of_year
+= gap
;
1686 day_of_year
+= start
;
1688 // Finally, pin to the real start and end of the month.
1689 if (day_of_year
< 1) day_of_year
= 1;
1690 if (day_of_year
> yearLen
) day_of_year
= yearLen
;
1692 // Make sure that the year and day of year are attended to by
1693 // clearing other fields which would normally take precedence.
1694 // If the disambiguation algorithm is changed, this section will
1695 // have to be updated as well.
1696 set(UCAL_DAY_OF_YEAR
, day_of_year
);
1700 case UCAL_DAY_OF_YEAR
:
1702 // Roll the day of year using millis. Compute the millis for
1703 // the start of the year, and get the length of the year.
1704 double delta
= amount
* kOneDay
; // Scale up from days to millis
1705 double min2
= internalGet(UCAL_DAY_OF_YEAR
)-1;
1707 min2
= internalGetTime() - min2
;
1709 // double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay;
1712 double yearLength
= getActualMaximum(UCAL_DAY_OF_YEAR
,status
);
1713 double oneYear
= yearLength
;
1715 newtime
= uprv_fmod((internalGetTime() + delta
- min2
), oneYear
);
1716 if (newtime
< 0) newtime
+= oneYear
;
1717 setTimeInMillis(newtime
+ min2
, status
);
1720 case UCAL_DAY_OF_WEEK
:
1721 case UCAL_DOW_LOCAL
:
1723 // Roll the day of week using millis. Compute the millis for
1724 // the start of the week, using the first day of week setting.
1725 // Restrict the millis to [start, start+7days).
1726 double delta
= amount
* kOneDay
; // Scale up from days to millis
1727 // Compute the number of days before the current day in this
1728 // week. This will be a value 0..6.
1729 int32_t leadDays
= internalGet(field
);
1730 leadDays
-= (field
== UCAL_DAY_OF_WEEK
) ? getFirstDayOfWeek() : 1;
1731 if (leadDays
< 0) leadDays
+= 7;
1732 double min2
= internalGetTime() - leadDays
* kOneDay
;
1733 double newtime
= uprv_fmod((internalGetTime() + delta
- min2
), kOneWeek
);
1734 if (newtime
< 0) newtime
+= kOneWeek
;
1735 setTimeInMillis(newtime
+ min2
, status
);
1738 case UCAL_DAY_OF_WEEK_IN_MONTH
:
1740 // Roll the day of week in the month using millis. Determine
1741 // the first day of the week in the month, and then the last,
1742 // and then roll within that range.
1743 double delta
= amount
* kOneWeek
; // Scale up from weeks to millis
1744 // Find the number of same days of the week before this one
1746 int32_t preWeeks
= (internalGet(UCAL_DAY_OF_MONTH
) - 1) / 7;
1747 // Find the number of same days of the week after this one
1749 int32_t postWeeks
= (getActualMaximum(UCAL_DAY_OF_MONTH
,status
) -
1750 internalGet(UCAL_DAY_OF_MONTH
)) / 7;
1751 // From these compute the min and gap millis for rolling.
1752 double min2
= internalGetTime() - preWeeks
* kOneWeek
;
1753 double gap2
= kOneWeek
* (preWeeks
+ postWeeks
+ 1); // Must add 1!
1754 // Roll within this range
1755 double newtime
= uprv_fmod((internalGetTime() + delta
- min2
), gap2
);
1756 if (newtime
< 0) newtime
+= gap2
;
1757 setTimeInMillis(newtime
+ min2
, status
);
1760 case UCAL_JULIAN_DAY
:
1761 set(field
, internalGet(field
) + amount
);
1764 // Other fields cannot be rolled by this method
1765 #if defined (U_DEBUG_CAL)
1766 fprintf(stderr
, "%s:%d: ILLEGAL ARG because of roll on non-rollable field %s\n",
1767 __FILE__
, __LINE__
,fldName(field
));
1769 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1773 void Calendar::add(EDateFields field
, int32_t amount
, UErrorCode
& status
)
1775 Calendar::add((UCalendarDateFields
)field
, amount
, status
);
1778 // -------------------------------------
1779 void Calendar::add(UCalendarDateFields field
, int32_t amount
, UErrorCode
& status
)
1782 return; // Do nothing!
1785 // We handle most fields in the same way. The algorithm is to add
1786 // a computed amount of millis to the current millis. The only
1787 // wrinkle is with DST -- for some fields, like the DAY_OF_MONTH,
1788 // we don't want the HOUR to shift due to changes in DST. If the
1789 // result of the add operation is to move from DST to Standard, or
1790 // vice versa, we need to adjust by an hour forward or back,
1791 // respectively. For such fields we set keepHourInvariant to TRUE.
1793 // We only adjust the DST for fields larger than an hour. For
1794 // fields smaller than an hour, we cannot adjust for DST without
1795 // causing problems. for instance, if you add one hour to April 5,
1796 // 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an
1797 // illegal value), but then the adjustment sees the change and
1798 // compensates by subtracting an hour. As a result the time
1799 // doesn't advance at all.
1801 // For some fields larger than a day, such as a UCAL_MONTH, we pin the
1802 // UCAL_DAY_OF_MONTH. This allows <March 31>.add(UCAL_MONTH, 1) to be
1803 // <April 30>, rather than <April 31> => <May 1>.
1805 double delta
= amount
; // delta in ms
1806 UBool keepHourInvariant
= TRUE
;
1810 set(field
, get(field
, status
) + amount
);
1811 pinField(UCAL_ERA
, status
);
1815 case UCAL_EXTENDED_YEAR
:
1818 set(field
, get(field
, status
) + amount
);
1819 pinField(UCAL_DAY_OF_MONTH
, status
);
1822 case UCAL_WEEK_OF_YEAR
:
1823 case UCAL_WEEK_OF_MONTH
:
1824 case UCAL_DAY_OF_WEEK_IN_MONTH
:
1829 delta
*= 12 * kOneHour
;
1832 case UCAL_DAY_OF_MONTH
:
1833 case UCAL_DAY_OF_YEAR
:
1834 case UCAL_DAY_OF_WEEK
:
1835 case UCAL_DOW_LOCAL
:
1836 case UCAL_JULIAN_DAY
:
1840 case UCAL_HOUR_OF_DAY
:
1843 keepHourInvariant
= FALSE
;
1847 delta
*= kOneMinute
;
1848 keepHourInvariant
= FALSE
;
1852 delta
*= kOneSecond
;
1853 keepHourInvariant
= FALSE
;
1856 case UCAL_MILLISECOND
:
1857 case UCAL_MILLISECONDS_IN_DAY
:
1858 keepHourInvariant
= FALSE
;
1862 #if defined (U_DEBUG_CAL)
1863 fprintf(stderr
, "%s:%d: ILLEGAL ARG because field %s not addable",
1864 __FILE__
, __LINE__
, fldName(field
));
1866 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1868 // throw new IllegalArgumentException("Calendar.add(" + fieldName(field) +
1869 // ") not supported");
1872 // In order to keep the hour invariant (for fields where this is
1873 // appropriate), record the DST_OFFSET before and after the add()
1874 // operation. If it has changed, then adjust the millis to
1878 if (keepHourInvariant
) {
1879 dst
= get(UCAL_DST_OFFSET
, status
);
1880 hour
= internalGet(UCAL_HOUR_OF_DAY
);
1883 setTimeInMillis(getTimeInMillis(status
) + delta
, status
);
1885 if (keepHourInvariant
) {
1886 dst
-= get(UCAL_DST_OFFSET
, status
);
1888 // We have done an hour-invariant adjustment but the
1889 // DST offset has altered. We adjust millis to keep
1890 // the hour constant. In cases such as midnight after
1891 // a DST change which occurs at midnight, there is the
1892 // danger of adjusting into a different day. To avoid
1893 // this we make the adjustment only if it actually
1894 // maintains the hour.
1895 double t
= internalGetTime();
1896 setTimeInMillis(t
+ dst
, status
);
1897 if (get(UCAL_HOUR_OF_DAY
, status
) != hour
) {
1898 setTimeInMillis(t
, status
);
1904 // -------------------------------------
1905 int32_t Calendar::fieldDifference(UDate when
, EDateFields field
, UErrorCode
& status
) {
1906 return fieldDifference(when
, (UCalendarDateFields
) field
, status
);
1909 int32_t Calendar::fieldDifference(UDate targetMs
, UCalendarDateFields field
, UErrorCode
& ec
) {
1910 if (U_FAILURE(ec
)) return 0;
1912 double startMs
= getTimeInMillis(ec
);
1913 // Always add from the start millis. This accomodates
1914 // operations like adding years from February 29, 2000 up to
1915 // February 29, 2004. If 1, 1, 1, 1 is added to the year
1916 // field, the DOM gets pinned to 28 and stays there, giving an
1917 // incorrect DOM difference of 1. We have to add 1, reset, 2,
1918 // reset, 3, reset, 4.
1919 if (startMs
< targetMs
) {
1921 // Find a value that is too large
1922 while (U_SUCCESS(ec
)) {
1923 setTimeInMillis(startMs
, ec
);
1924 add(field
, max
, ec
);
1925 double ms
= getTimeInMillis(ec
);
1926 if (ms
== targetMs
) {
1928 } else if (ms
> targetMs
) {
1934 // Field difference too large to fit into int32_t
1935 #if defined (U_DEBUG_CAL)
1936 fprintf(stderr
, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
1937 __FILE__
, __LINE__
, fldName(field
));
1939 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
1943 // Do a binary search
1944 while ((max
- min
) > 1 && U_SUCCESS(ec
)) {
1945 int32_t t
= (min
+ max
) / 2;
1946 setTimeInMillis(startMs
, ec
);
1948 double ms
= getTimeInMillis(ec
);
1949 if (ms
== targetMs
) {
1951 } else if (ms
> targetMs
) {
1957 } else if (startMs
> targetMs
) {
1959 // Find a value that is too small
1960 while (U_SUCCESS(ec
)) {
1961 setTimeInMillis(startMs
, ec
);
1962 add(field
, max
, ec
);
1963 double ms
= getTimeInMillis(ec
);
1964 if (ms
== targetMs
) {
1966 } else if (ms
< targetMs
) {
1972 // Field difference too large to fit into int32_t
1973 #if defined (U_DEBUG_CAL)
1974 fprintf(stderr
, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
1975 __FILE__
, __LINE__
, fldName(field
));
1977 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
1981 // Do a binary search
1982 while ((min
- max
) > 1 && U_SUCCESS(ec
)) {
1983 int32_t t
= (min
+ max
) / 2;
1984 setTimeInMillis(startMs
, ec
);
1986 double ms
= getTimeInMillis(ec
);
1987 if (ms
== targetMs
) {
1989 } else if (ms
< targetMs
) {
1996 // Set calendar to end point
1997 setTimeInMillis(startMs
, ec
);
1998 add(field
, min
, ec
);
2000 /* Test for buffer overflows */
2007 // -------------------------------------
2010 Calendar::adoptTimeZone(TimeZone
* zone
)
2012 // Do nothing if passed-in zone is NULL
2013 if (zone
== NULL
) return;
2015 // fZone should always be non-null
2016 if (fZone
!= NULL
) delete fZone
;
2019 // if the zone changes, we need to recompute the time fields
2020 fAreFieldsSet
= FALSE
;
2023 // -------------------------------------
2025 Calendar::setTimeZone(const TimeZone
& zone
)
2027 adoptTimeZone(zone
.clone());
2030 // -------------------------------------
2033 Calendar::getTimeZone() const
2038 // -------------------------------------
2041 Calendar::orphanTimeZone()
2043 TimeZone
*z
= fZone
;
2044 // we let go of the time zone; the new time zone is the system default time zone
2045 fZone
= TimeZone::createDefault();
2049 // -------------------------------------
2052 Calendar::setLenient(UBool lenient
)
2057 // -------------------------------------
2060 Calendar::isLenient() const
2065 // -------------------------------------
2068 Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value
)
2070 if (fFirstDayOfWeek
!= value
&&
2071 value
>= UCAL_SUNDAY
&& value
<= UCAL_SATURDAY
) {
2072 fFirstDayOfWeek
= value
;
2073 fAreFieldsSet
= FALSE
;
2077 // -------------------------------------
2079 Calendar::EDaysOfWeek
2080 Calendar::getFirstDayOfWeek() const
2082 return (Calendar::EDaysOfWeek
)fFirstDayOfWeek
;
2086 Calendar::getFirstDayOfWeek(UErrorCode
& /*status*/) const
2088 return fFirstDayOfWeek
;
2090 // -------------------------------------
2093 Calendar::setMinimalDaysInFirstWeek(uint8_t value
)
2095 // Values less than 1 have the same effect as 1; values greater
2096 // than 7 have the same effect as 7. However, we normalize values
2097 // so operator== and so forth work.
2100 } else if (value
> 7) {
2103 if (fMinimalDaysInFirstWeek
!= value
) {
2104 fMinimalDaysInFirstWeek
= value
;
2105 fAreFieldsSet
= FALSE
;
2109 // -------------------------------------
2112 Calendar::getMinimalDaysInFirstWeek() const
2114 return fMinimalDaysInFirstWeek
;
2117 // -------------------------------------
2118 // weekend functions, just dummy implementations for now (for API freeze)
2120 UCalendarWeekdayType
2121 Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek
, UErrorCode
&status
) const
2123 if (U_FAILURE(status
)) {
2124 return UCAL_WEEKDAY
;
2126 if (dayOfWeek
< UCAL_SUNDAY
|| dayOfWeek
> UCAL_SATURDAY
) {
2127 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2128 return UCAL_WEEKDAY
;
2130 if (fWeekendOnset
< fWeekendCease
) {
2131 if (dayOfWeek
< fWeekendOnset
|| dayOfWeek
> fWeekendCease
) {
2132 return UCAL_WEEKDAY
;
2135 if (dayOfWeek
> fWeekendCease
&& dayOfWeek
< fWeekendOnset
) {
2136 return UCAL_WEEKDAY
;
2139 if (dayOfWeek
== fWeekendOnset
) {
2140 return (fWeekendOnsetMillis
== 0) ? UCAL_WEEKEND
: UCAL_WEEKEND_ONSET
;
2142 if (dayOfWeek
== fWeekendCease
) {
2143 return (fWeekendCeaseMillis
== 0) ? UCAL_WEEKDAY
: UCAL_WEEKEND_CEASE
;
2145 return UCAL_WEEKEND
;
2149 Calendar::getWeekendTransition(UCalendarDaysOfWeek dayOfWeek
, UErrorCode
&status
) const
2151 if (U_FAILURE(status
)) {
2154 if (dayOfWeek
== fWeekendOnset
) {
2155 return fWeekendOnsetMillis
;
2156 } else if (dayOfWeek
== fWeekendCease
) {
2157 return fWeekendCeaseMillis
;
2159 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2164 Calendar::isWeekend(UDate date
, UErrorCode
&status
) const
2166 if (U_FAILURE(status
)) {
2169 // clone the calendar so we don't mess with the real one.
2170 Calendar
*work
= (Calendar
*)this->clone();
2172 status
= U_MEMORY_ALLOCATION_ERROR
;
2175 UBool result
= FALSE
;
2176 work
->setTime(date
, status
);
2177 if (U_SUCCESS(status
)) {
2178 result
= work
->isWeekend();
2185 Calendar::isWeekend(void) const
2187 UErrorCode status
= U_ZERO_ERROR
;
2188 UCalendarDaysOfWeek dayOfWeek
= (UCalendarDaysOfWeek
)get(UCAL_DAY_OF_WEEK
, status
);
2189 UCalendarWeekdayType dayType
= getDayOfWeekType(dayOfWeek
, status
);
2190 if (U_SUCCESS(status
)) {
2196 case UCAL_WEEKEND_ONSET
:
2197 case UCAL_WEEKEND_CEASE
:
2198 // Use internalGet() because the above call to get() populated all fields.
2200 int32_t millisInDay
= internalGet(UCAL_MILLISECONDS_IN_DAY
);
2201 int32_t transitionMillis
= getWeekendTransition(dayOfWeek
, status
);
2202 if (U_SUCCESS(status
)) {
2203 return (dayType
== UCAL_WEEKEND_ONSET
)?
2204 (millisInDay
>= transitionMillis
):
2205 (millisInDay
< transitionMillis
);
2207 // else fall through, return FALSE
2216 // ------------------------------------- limits
2219 Calendar::getMinimum(EDateFields field
) const {
2220 return getLimit((UCalendarDateFields
) field
,UCAL_LIMIT_MINIMUM
);
2224 Calendar::getMinimum(UCalendarDateFields field
) const
2226 return getLimit(field
,UCAL_LIMIT_MINIMUM
);
2229 // -------------------------------------
2231 Calendar::getMaximum(EDateFields field
) const
2233 return getLimit((UCalendarDateFields
) field
,UCAL_LIMIT_MAXIMUM
);
2237 Calendar::getMaximum(UCalendarDateFields field
) const
2239 return getLimit(field
,UCAL_LIMIT_MAXIMUM
);
2242 // -------------------------------------
2244 Calendar::getGreatestMinimum(EDateFields field
) const
2246 return getLimit((UCalendarDateFields
)field
,UCAL_LIMIT_GREATEST_MINIMUM
);
2250 Calendar::getGreatestMinimum(UCalendarDateFields field
) const
2252 return getLimit(field
,UCAL_LIMIT_GREATEST_MINIMUM
);
2255 // -------------------------------------
2257 Calendar::getLeastMaximum(EDateFields field
) const
2259 return getLimit((UCalendarDateFields
) field
,UCAL_LIMIT_LEAST_MAXIMUM
);
2263 Calendar::getLeastMaximum(UCalendarDateFields field
) const
2265 return getLimit( field
,UCAL_LIMIT_LEAST_MAXIMUM
);
2268 // -------------------------------------
2270 Calendar::getActualMinimum(EDateFields field
, UErrorCode
& status
) const
2272 return getActualMinimum((UCalendarDateFields
) field
, status
);
2275 int32_t Calendar::getLimit(UCalendarDateFields field
, ELimitType limitType
) const {
2277 case UCAL_DAY_OF_WEEK
:
2280 case UCAL_HOUR_OF_DAY
:
2283 case UCAL_MILLISECOND
:
2284 case UCAL_ZONE_OFFSET
:
2285 case UCAL_DST_OFFSET
:
2286 case UCAL_DOW_LOCAL
:
2287 case UCAL_JULIAN_DAY
:
2288 case UCAL_MILLISECONDS_IN_DAY
:
2289 case UCAL_IS_LEAP_MONTH
:
2290 return kCalendarLimits
[field
][limitType
];
2292 case UCAL_WEEK_OF_MONTH
:
2295 if (limitType
== UCAL_LIMIT_MINIMUM
) {
2296 limit
= getMinimalDaysInFirstWeek() == 1 ? 1 : 0;
2297 } else if (limitType
== UCAL_LIMIT_GREATEST_MINIMUM
) {
2300 int32_t minDaysInFirst
= getMinimalDaysInFirstWeek();
2301 int32_t daysInMonth
= handleGetLimit(UCAL_DAY_OF_MONTH
, limitType
);
2302 if (limitType
== UCAL_LIMIT_LEAST_MAXIMUM
) {
2303 limit
= (daysInMonth
+ (7 - minDaysInFirst
)) / 7;
2304 } else { // limitType == UCAL_LIMIT_MAXIMUM
2305 limit
= (daysInMonth
+ 6 + (7 - minDaysInFirst
)) / 7;
2311 return handleGetLimit(field
, limitType
);
2317 Calendar::getActualMinimum(UCalendarDateFields field
, UErrorCode
& status
) const
2319 int32_t fieldValue
= getGreatestMinimum(field
);
2320 int32_t endValue
= getMinimum(field
);
2322 // if we know that the minimum value is always the same, just return it
2323 if (fieldValue
== endValue
) {
2327 // clone the calendar so we don't mess with the real one, and set it to
2328 // accept anything for the field values
2329 Calendar
*work
= (Calendar
*)this->clone();
2331 status
= U_MEMORY_ALLOCATION_ERROR
;
2334 work
->setLenient(TRUE
);
2336 // now try each value from getLeastMaximum() to getMaximum() one by one until
2337 // we get a value that normalizes to another value. The last value that
2338 // normalizes to itself is the actual minimum for the current date
2339 int32_t result
= fieldValue
;
2342 work
->set(field
, fieldValue
);
2343 if (work
->get(field
, status
) != fieldValue
) {
2347 result
= fieldValue
;
2350 } while (fieldValue
>= endValue
);
2354 /* Test for buffer overflows */
2355 if(U_FAILURE(status
)) {
2361 // -------------------------------------
2366 * Ensure that each field is within its valid range by calling {@link
2367 * #validateField(int)} on each field that has been set. This method
2368 * should only be called if this calendar is not lenient.
2370 * @see #validateField(int)
2372 void Calendar::validateFields(UErrorCode
&status
) {
2373 for (int32_t field
= 0; U_SUCCESS(status
) && (field
< UCAL_FIELD_COUNT
); field
++) {
2374 if (isSet((UCalendarDateFields
)field
)) {
2375 validateField((UCalendarDateFields
)field
, status
);
2381 * Validate a single field of this calendar. Subclasses should
2382 * override this method to validate any calendar-specific fields.
2383 * Generic fields can be handled by
2384 * <code>Calendar.validateField()</code>.
2385 * @see #validateField(int, int, int)
2387 void Calendar::validateField(UCalendarDateFields field
, UErrorCode
&status
) {
2390 case UCAL_DAY_OF_MONTH
:
2391 y
= handleGetExtendedYear();
2392 validateField(field
, 1, handleGetMonthLength(y
, internalGet(UCAL_MONTH
)), status
);
2394 case UCAL_DAY_OF_YEAR
:
2395 y
= handleGetExtendedYear();
2396 validateField(field
, 1, handleGetYearLength(y
), status
);
2398 case UCAL_DAY_OF_WEEK_IN_MONTH
:
2399 if (internalGet(field
) == 0) {
2400 #if defined (U_DEBUG_CAL)
2401 fprintf(stderr
, "%s:%d: ILLEGAL ARG because DOW in month cannot be 0\n",
2402 __FILE__
, __LINE__
);
2404 status
= U_ILLEGAL_ARGUMENT_ERROR
; // "DAY_OF_WEEK_IN_MONTH cannot be zero"
2407 validateField(field
, getMinimum(field
), getMaximum(field
), status
);
2410 validateField(field
, getMinimum(field
), getMaximum(field
), status
);
2416 * Validate a single field of this calendar given its minimum and
2417 * maximum allowed value. If the field is out of range, throw a
2418 * descriptive <code>IllegalArgumentException</code>. Subclasses may
2419 * use this method in their implementation of {@link
2420 * #validateField(int)}.
2422 void Calendar::validateField(UCalendarDateFields field
, int32_t min
, int32_t max
, UErrorCode
& status
)
2424 int32_t value
= fFields
[field
];
2425 if (value
< min
|| value
> max
) {
2426 #if defined (U_DEBUG_CAL)
2427 fprintf(stderr
, "%s:%d: ILLEGAL ARG because of field %s out of range %d..%d at %d\n",
2428 __FILE__
, __LINE__
,fldName(field
),min
,max
,value
);
2430 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2435 // -------------------------
2437 const UFieldResolutionTable
* Calendar::getFieldResolutionTable() const {
2438 return kDatePrecedence
;
2442 UCalendarDateFields
Calendar::newerField(UCalendarDateFields defaultField
, UCalendarDateFields alternateField
) const
2444 if (fStamp
[alternateField
] > fStamp
[defaultField
]) {
2445 return alternateField
;
2447 return defaultField
;
2450 UCalendarDateFields
Calendar::resolveFields(const UFieldResolutionTable
* precedenceTable
) {
2451 int32_t bestField
= UCAL_FIELD_COUNT
;
2452 for (int32_t g
=0; precedenceTable
[g
][0][0] != -1 && (bestField
== UCAL_FIELD_COUNT
); ++g
) {
2453 int32_t bestStamp
= kUnset
;
2454 for (int32_t l
=0; precedenceTable
[g
][l
][0] != -1; ++l
) {
2455 int32_t lineStamp
= kUnset
;
2456 // Skip over first entry if it is negative
2457 for (int32_t i
=((precedenceTable
[g
][l
][0]>=kResolveRemap
)?1:0); precedenceTable
[g
][l
][i
]!=-1; ++i
) {
2458 int32_t s
= fStamp
[precedenceTable
[g
][l
][i
]];
2459 // If any field is unset then don't use this line
2462 } else if(s
> lineStamp
) {
2466 // Record new maximum stamp & field no.
2467 if (lineStamp
> bestStamp
) {
2468 bestStamp
= lineStamp
;
2469 bestField
= precedenceTable
[g
][l
][0]; // First field refers to entire line
2475 return (UCalendarDateFields
)( (bestField
>=kResolveRemap
)?(bestField
&(kResolveRemap
-1)):bestField
);
2478 const UFieldResolutionTable
Calendar::kDatePrecedence
[] =
2481 { UCAL_DAY_OF_MONTH
, kResolveSTOP
},
2482 { UCAL_WEEK_OF_YEAR
, UCAL_DAY_OF_WEEK
, kResolveSTOP
},
2483 { UCAL_WEEK_OF_MONTH
, UCAL_DAY_OF_WEEK
, kResolveSTOP
},
2484 { UCAL_DAY_OF_WEEK_IN_MONTH
, UCAL_DAY_OF_WEEK
, kResolveSTOP
},
2485 { UCAL_WEEK_OF_YEAR
, UCAL_DOW_LOCAL
, kResolveSTOP
},
2486 { UCAL_WEEK_OF_MONTH
, UCAL_DOW_LOCAL
, kResolveSTOP
},
2487 { UCAL_DAY_OF_WEEK_IN_MONTH
, UCAL_DOW_LOCAL
, kResolveSTOP
},
2488 { UCAL_DAY_OF_YEAR
, kResolveSTOP
},
2489 { kResolveRemap
| UCAL_DAY_OF_MONTH
, UCAL_YEAR
, kResolveSTOP
}, // if YEAR is set over YEAR_WOY use DAY_OF_MONTH
2490 { kResolveRemap
| UCAL_WEEK_OF_YEAR
, UCAL_YEAR_WOY
, kResolveSTOP
}, // if YEAR_WOY is set, calc based on WEEK_OF_YEAR
2494 { UCAL_WEEK_OF_YEAR
, kResolveSTOP
},
2495 { UCAL_WEEK_OF_MONTH
, kResolveSTOP
},
2496 { UCAL_DAY_OF_WEEK_IN_MONTH
, kResolveSTOP
},
2497 { kResolveRemap
| UCAL_DAY_OF_WEEK_IN_MONTH
, UCAL_DAY_OF_WEEK
, kResolveSTOP
},
2498 { kResolveRemap
| UCAL_DAY_OF_WEEK_IN_MONTH
, UCAL_DOW_LOCAL
, kResolveSTOP
},
2505 const UFieldResolutionTable
Calendar::kDOWPrecedence
[] =
2508 { UCAL_DAY_OF_WEEK
,kResolveSTOP
, kResolveSTOP
},
2509 { UCAL_DOW_LOCAL
,kResolveSTOP
, kResolveSTOP
},
2515 // precedence for calculating a year
2516 const UFieldResolutionTable
Calendar::kYearPrecedence
[] =
2519 { UCAL_YEAR
, kResolveSTOP
},
2520 { UCAL_EXTENDED_YEAR
, kResolveSTOP
},
2521 { UCAL_YEAR_WOY
, UCAL_WEEK_OF_YEAR
, kResolveSTOP
}, // YEAR_WOY is useless without WEEK_OF_YEAR
2528 // -------------------------
2531 void Calendar::computeTime(UErrorCode
& status
) {
2533 validateFields(status
);
2534 if (U_FAILURE(status
)) {
2539 // Compute the Julian day
2540 int32_t julianDay
= computeJulianDay();
2542 double millis
= Grego::julianDayToMillis(julianDay
);
2544 #if defined (U_DEBUG_CAL)
2545 // int32_t julianInsanityCheck = (int32_t)ClockMath::floorDivide(millis, kOneDay);
2546 // julianInsanityCheck += kEpochStartAsJulianDay;
2547 // if(1 || julianInsanityCheck != julianDay) {
2548 // fprintf(stderr, "%s:%d- D'oh- computed jules %d, to mills (%s)%.lf, recomputed %d\n",
2549 // __FILE__, __LINE__, julianDay, millis<0.0?"NEG":"", millis, julianInsanityCheck);
2553 int32_t millisInDay
;
2555 // We only use MILLISECONDS_IN_DAY if it has been set by the user.
2556 // This makes it possible for the caller to set the calendar to a
2557 // time and call clear(MONTH) to reset the MONTH to January. This
2558 // is legacy behavior. Without this, clear(MONTH) has no effect,
2559 // since the internally set JULIAN_DAY is used.
2560 if (fStamp
[UCAL_MILLISECONDS_IN_DAY
] >= ((int32_t)kMinimumUserStamp
) &&
2561 newestStamp(UCAL_AM_PM
, UCAL_MILLISECOND
, kUnset
) <= fStamp
[UCAL_MILLISECONDS_IN_DAY
]) {
2562 millisInDay
= internalGet(UCAL_MILLISECONDS_IN_DAY
);
2564 millisInDay
= computeMillisInDay();
2567 // Compute the time zone offset and DST offset. There are two potential
2568 // ambiguities here. We'll assume a 2:00 am (wall time) switchover time
2569 // for discussion purposes here.
2570 // 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
2571 // can be in standard or in DST depending. However, 2:00 am is an invalid
2572 // representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
2573 // We assume standard time, that is, 2:30 am is interpreted as 3:30 am DST.
2574 // 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
2575 // can be in standard or DST. Both are valid representations (the rep
2576 // jumps from 1:59:59 DST to 1:00:00 Std).
2577 // Again, we assume standard time, that is, 1:30 am is interpreted as 1:30 am Std.
2578 // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
2579 // or DST_OFFSET fields; then we use those fields.
2580 if (fStamp
[UCAL_ZONE_OFFSET
] >= ((int32_t)kMinimumUserStamp
) ||
2581 fStamp
[UCAL_DST_OFFSET
] >= ((int32_t)kMinimumUserStamp
)) {
2582 millisInDay
-= internalGet(UCAL_ZONE_OFFSET
) + internalGet(UCAL_DST_OFFSET
);
2584 millisInDay
-= computeZoneOffset(millis
, millisInDay
,status
);
2587 internalSetTime(millis
+ millisInDay
);
2591 * Compute the milliseconds in the day from the fields. This is a
2592 * value from 0 to 23:59:59.999 inclusive, unless fields are out of
2593 * range, in which case it can be an arbitrary value. This value
2594 * reflects local zone wall time.
2597 int32_t Calendar::computeMillisInDay() {
2598 // Do the time portion of the conversion.
2600 int32_t millisInDay
= 0;
2602 // Find the best set of fields specifying the time of day. There
2603 // are only two possibilities here; the HOUR_OF_DAY or the
2604 // AM_PM and the HOUR.
2605 int32_t hourOfDayStamp
= fStamp
[UCAL_HOUR_OF_DAY
];
2606 int32_t hourStamp
= (fStamp
[UCAL_HOUR
] > fStamp
[UCAL_AM_PM
])?fStamp
[UCAL_HOUR
]:fStamp
[UCAL_AM_PM
];
2607 int32_t bestStamp
= (hourStamp
> hourOfDayStamp
) ? hourStamp
: hourOfDayStamp
;
2610 if (bestStamp
!= kUnset
) {
2611 if (bestStamp
== hourOfDayStamp
) {
2612 // Don't normalize here; let overflow bump into the next period.
2613 // This is consistent with how we handle other fields.
2614 millisInDay
+= internalGet(UCAL_HOUR_OF_DAY
);
2616 // Don't normalize here; let overflow bump into the next period.
2617 // This is consistent with how we handle other fields.
2618 millisInDay
+= internalGet(UCAL_HOUR
);
2619 millisInDay
+= 12 * internalGet(UCAL_AM_PM
); // Default works for unset AM_PM
2623 // We use the fact that unset == 0; we start with millisInDay
2626 millisInDay
+= internalGet(UCAL_MINUTE
); // now have minutes
2628 millisInDay
+= internalGet(UCAL_SECOND
); // now have seconds
2629 millisInDay
*= 1000;
2630 millisInDay
+= internalGet(UCAL_MILLISECOND
); // now have millis
2636 * This method can assume EXTENDED_YEAR has been set.
2637 * @param millis milliseconds of the date fields
2638 * @param millisInDay milliseconds of the time fields; may be out
2642 int32_t Calendar::computeZoneOffset(double millis
, int32_t millisInDay
, UErrorCode
&ec
) {
2643 int32_t rawOffset
, dstOffset
;
2644 getTimeZone().getOffset(millis
+millisInDay
, TRUE
, rawOffset
, dstOffset
, ec
);
2645 return rawOffset
+ dstOffset
;
2646 // Note: Because we pass in wall millisInDay, rather than
2647 // standard millisInDay, we interpret "1:00 am" on the day
2648 // of cessation of DST as "1:00 am Std" (assuming the time
2649 // of cessation is 2:00 am).
2652 int32_t Calendar::computeJulianDay()
2654 // We want to see if any of the date fields is newer than the
2655 // JULIAN_DAY. If not, then we use JULIAN_DAY. If so, then we do
2656 // the normal resolution. We only use JULIAN_DAY if it has been
2657 // set by the user. This makes it possible for the caller to set
2658 // the calendar to a time and call clear(MONTH) to reset the MONTH
2659 // to January. This is legacy behavior. Without this,
2660 // clear(MONTH) has no effect, since the internally set JULIAN_DAY
2662 if (fStamp
[UCAL_JULIAN_DAY
] >= (int32_t)kMinimumUserStamp
) {
2663 int32_t bestStamp
= newestStamp(UCAL_ERA
, UCAL_DAY_OF_WEEK_IN_MONTH
, kUnset
);
2664 bestStamp
= newestStamp(UCAL_YEAR_WOY
, UCAL_EXTENDED_YEAR
, bestStamp
);
2665 if (bestStamp
<= fStamp
[UCAL_JULIAN_DAY
]) {
2666 return internalGet(UCAL_JULIAN_DAY
);
2670 UCalendarDateFields bestField
= resolveFields(getFieldResolutionTable());
2671 if (bestField
== UCAL_FIELD_COUNT
) {
2672 bestField
= UCAL_DAY_OF_MONTH
;
2675 return handleComputeJulianDay(bestField
);
2678 // -------------------------------------------
2680 int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField
) {
2681 UBool useMonth
= (bestField
== UCAL_DAY_OF_MONTH
||
2682 bestField
== UCAL_WEEK_OF_MONTH
||
2683 bestField
== UCAL_DAY_OF_WEEK_IN_MONTH
);
2686 if (bestField
== UCAL_WEEK_OF_YEAR
) {
2687 year
= internalGet(UCAL_YEAR_WOY
, handleGetExtendedYear());
2688 internalSet(UCAL_EXTENDED_YEAR
, year
);
2690 year
= handleGetExtendedYear();
2691 internalSet(UCAL_EXTENDED_YEAR
, year
);
2694 #if defined (U_DEBUG_CAL)
2695 fprintf(stderr
, "%s:%d: bestField= %s - y=%d\n", __FILE__
, __LINE__
, fldName(bestField
), year
);
2698 // Get the Julian day of the day BEFORE the start of this year.
2699 // If useMonth is true, get the day before the start of the month.
2701 // give calendar subclass a chance to have a default 'first' month
2704 if(isSet(UCAL_MONTH
)) {
2705 month
= internalGet(UCAL_MONTH
);
2707 month
= getDefaultMonthInYear(year
);
2710 int32_t julianDay
= handleComputeMonthStart(year
, useMonth
? month
: 0, useMonth
);
2712 if (bestField
== UCAL_DAY_OF_MONTH
) {
2714 // give calendar subclass a chance to have a default 'first' dom
2716 if(isSet(UCAL_DAY_OF_MONTH
)) {
2717 dayOfMonth
= internalGet(UCAL_DAY_OF_MONTH
,1);
2719 dayOfMonth
= getDefaultDayInMonth(year
, month
);
2721 return julianDay
+ dayOfMonth
;
2724 if (bestField
== UCAL_DAY_OF_YEAR
) {
2725 return julianDay
+ internalGet(UCAL_DAY_OF_YEAR
);
2728 int32_t firstDayOfWeek
= getFirstDayOfWeek(); // Localized fdw
2730 // At this point julianDay is the 0-based day BEFORE the first day of
2731 // January 1, year 1 of the given calendar. If julianDay == 0, it
2732 // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
2733 // or Gregorian). (or it is before the month we are in, if useMonth is True)
2735 // At this point we need to process the WEEK_OF_MONTH or
2736 // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
2737 // First, perform initial shared computations. These locate the
2738 // first week of the period.
2740 // Get the 0-based localized DOW of day one of the month or year.
2741 // Valid range 0..6.
2742 int32_t first
= julianDayToDayOfWeek(julianDay
+ 1) - firstDayOfWeek
;
2747 int32_t dowLocal
= getLocalDOW();
2749 // Find the first target DOW (dowLocal) in the month or year.
2750 // Actually, it may be just before the first of the month or year.
2751 // It will be an integer from -5..7.
2752 int32_t date
= 1 - first
+ dowLocal
;
2754 if (bestField
== UCAL_DAY_OF_WEEK_IN_MONTH
) {
2755 // Adjust the target DOW to be in the month or year.
2760 // The only trickiness occurs if the day-of-week-in-month is
2762 int32_t dim
= internalGet(UCAL_DAY_OF_WEEK_IN_MONTH
, 1);
2764 date
+= 7*(dim
- 1);
2767 // Move date to the last of this day-of-week in this month,
2768 // then back up as needed. If dim==-1, we don't back up at
2769 // all. If dim==-2, we back up once, etc. Don't back up
2770 // past the first of the given day-of-week in this month.
2771 // Note that we handle -2, -3, etc. correctly, even though
2772 // values < -1 are technically disallowed.
2773 int32_t m
= internalGet(UCAL_MONTH
, UCAL_JANUARY
);
2774 int32_t monthLength
= handleGetMonthLength(year
, m
);
2775 date
+= ((monthLength
- date
) / 7 + dim
+ 1) * 7;
2778 #if defined (U_DEBUG_CAL)
2779 fprintf(stderr
, "%s:%d - bf= %s\n", __FILE__
, __LINE__
, fldName(bestField
));
2782 if(bestField
== UCAL_WEEK_OF_YEAR
) { // ------------------------------------- WOY -------------
2783 if(!isSet(UCAL_YEAR_WOY
) || // YWOY not set at all or
2784 ( (resolveFields(kYearPrecedence
) != UCAL_YEAR_WOY
) // YWOY doesn't have precedence
2785 && (fStamp
[UCAL_YEAR_WOY
]!=kInternallySet
) ) ) // (excluding where all fields are internally set - then YWOY is used)
2787 // need to be sure to stay in 'real' year.
2788 int32_t woy
= internalGet(bestField
);
2790 int32_t nextJulianDay
= handleComputeMonthStart(year
+1, 0, FALSE
); // jd of day before jan 1
2791 int32_t nextFirst
= julianDayToDayOfWeek(nextJulianDay
+ 1) - firstDayOfWeek
;
2793 if (nextFirst
< 0) { // 0..6 ldow of Jan 1
2797 if(woy
==1) { // FIRST WEEK ---------------------------------
2798 #if defined (U_DEBUG_CAL)
2799 fprintf(stderr
, "%s:%d - woy=%d, yp=%d, nj(%d)=%d, nf=%d", __FILE__
, __LINE__
,
2800 internalGet(bestField
), resolveFields(kYearPrecedence
), year
+1,
2801 nextJulianDay
, nextFirst
);
2803 fprintf(stderr
, " next: %d DFW, min=%d \n", (7-nextFirst
), getMinimalDaysInFirstWeek() );
2806 // nextFirst is now the localized DOW of Jan 1 of y-woy+1
2807 if((nextFirst
> 0) && // Jan 1 starts on FDOW
2808 (7-nextFirst
) >= getMinimalDaysInFirstWeek()) // or enough days in the week
2810 // Jan 1 of (yearWoy+1) is in yearWoy+1 - recalculate JD to next year
2811 #if defined (U_DEBUG_CAL)
2812 fprintf(stderr
, "%s:%d - was going to move JD from %d to %d [d%d]\n", __FILE__
, __LINE__
,
2813 julianDay
, nextJulianDay
, (nextJulianDay
-julianDay
));
2815 julianDay
= nextJulianDay
;
2817 // recalculate 'first' [0-based local dow of jan 1]
2818 first
= julianDayToDayOfWeek(julianDay
+ 1) - firstDayOfWeek
;
2822 // recalculate date.
2823 date
= 1 - first
+ dowLocal
;
2825 } else if(woy
>=getLeastMaximum(bestField
)) {
2826 // could be in the last week- find out if this JD would overstep
2827 int32_t testDate
= date
;
2828 if ((7 - first
) < getMinimalDaysInFirstWeek()) {
2832 // Now adjust for the week number.
2833 testDate
+= 7 * (woy
- 1);
2835 #if defined (U_DEBUG_CAL)
2836 fprintf(stderr
, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n",
2837 __FILE__
, __LINE__
, year
, year
-1, testDate
, julianDay
+testDate
, nextJulianDay
);
2839 if(julianDay
+testDate
> nextJulianDay
) { // is it past Dec 31? (nextJulianDay is day BEFORE year+1's Jan 1)
2840 // Fire up the calculating engines.. retry YWOY = (year-1)
2841 julianDay
= handleComputeMonthStart(year
-1, 0, FALSE
); // jd before Jan 1 of previous year
2842 first
= julianDayToDayOfWeek(julianDay
+ 1) - firstDayOfWeek
; // 0 based local dow of first week
2844 if(first
< 0) { // 0..6
2847 date
= 1 - first
+ dowLocal
;
2849 #if defined (U_DEBUG_CAL)
2850 fprintf(stderr
, "%s:%d - date now %d, jd%d, ywoy%d\n",
2851 __FILE__
, __LINE__
, date
, julianDay
, year
-1);
2855 } /* correction needed */
2856 } /* leastmaximum */
2857 } /* resolvefields(year) != year_woy */
2858 } /* bestfield != week_of_year */
2860 // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR)
2861 // Adjust for minimal days in first week
2862 if ((7 - first
) < getMinimalDaysInFirstWeek()) {
2866 // Now adjust for the week number.
2867 date
+= 7 * (internalGet(bestField
) - 1);
2870 return julianDay
+ date
;
2874 Calendar::getDefaultMonthInYear(int32_t /*eyear*/)
2880 Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/)
2886 int32_t Calendar::getLocalDOW()
2888 // Get zero-based localized DOW, valid range 0..6. This is the DOW
2889 // we are looking for.
2890 int32_t dowLocal
= 0;
2891 switch (resolveFields(kDOWPrecedence
)) {
2892 case UCAL_DAY_OF_WEEK
:
2893 dowLocal
= internalGet(UCAL_DAY_OF_WEEK
) - fFirstDayOfWeek
;
2895 case UCAL_DOW_LOCAL
:
2896 dowLocal
= internalGet(UCAL_DOW_LOCAL
) - 1;
2901 dowLocal
= dowLocal
% 7;
2908 int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy
, int32_t woy
)
2910 // We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine
2911 // what year we fall in, so that other code can set it properly.
2912 // (code borrowed from computeWeekFields and handleComputeJulianDay)
2915 // First, we need a reliable DOW.
2916 UCalendarDateFields bestField
= resolveFields(kDatePrecedence
); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields
2919 int32_t dowLocal
= getLocalDOW(); // 0..6
2920 int32_t firstDayOfWeek
= getFirstDayOfWeek(); // Localized fdw
2921 int32_t jan1Start
= handleComputeMonthStart(yearWoy
, 0, FALSE
);
2922 int32_t nextJan1Start
= handleComputeMonthStart(yearWoy
+1, 0, FALSE
); // next year's Jan1 start
2924 // At this point julianDay is the 0-based day BEFORE the first day of
2925 // January 1, year 1 of the given calendar. If julianDay == 0, it
2926 // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
2927 // or Gregorian). (or it is before the month we are in, if useMonth is True)
2929 // At this point we need to process the WEEK_OF_MONTH or
2930 // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
2931 // First, perform initial shared computations. These locate the
2932 // first week of the period.
2934 // Get the 0-based localized DOW of day one of the month or year.
2935 // Valid range 0..6.
2936 int32_t first
= julianDayToDayOfWeek(jan1Start
+ 1) - firstDayOfWeek
;
2940 int32_t nextFirst
= julianDayToDayOfWeek(nextJan1Start
+ 1) - firstDayOfWeek
;
2941 if (nextFirst
< 0) {
2945 int32_t minDays
= getMinimalDaysInFirstWeek();
2946 UBool jan1InPrevYear
= FALSE
; // January 1st in the year of WOY is the 1st week? (i.e. first week is < minimal )
2947 //UBool nextJan1InPrevYear = FALSE; // January 1st of Year of WOY + 1 is in the first week?
2949 if((7 - first
) < minDays
) {
2950 jan1InPrevYear
= TRUE
;
2953 // if((7 - nextFirst) < minDays) {
2954 // nextJan1InPrevYear = TRUE;
2958 case UCAL_WEEK_OF_YEAR
:
2960 if(jan1InPrevYear
== TRUE
) {
2961 // the first week of January is in the previous year
2962 // therefore WOY1 is always solidly within yearWoy
2965 // First WOY is split between two years
2966 if( dowLocal
< first
) { // we are prior to Jan 1
2967 return yearWoy
-1; // previous year
2969 return yearWoy
; // in this year
2972 } else if(woy
>= getLeastMaximum(bestField
)) {
2973 // we _might_ be in the last week..
2974 int32_t jd
= // Calculate JD of our target day:
2975 jan1Start
+ // JD of Jan 1
2976 (7-first
) + // days in the first week (Jan 1.. )
2977 (woy
-1)*7 + // add the weeks of the year
2978 dowLocal
; // the local dow (0..6) of last week
2979 if(jan1InPrevYear
==FALSE
) {
2980 jd
-= 7; // woy already includes Jan 1's week.
2983 if( (jd
+1) >= nextJan1Start
) {
2984 // we are in week 52 or 53 etc. - actual year is yearWoy+1
2987 // still in yearWoy;
2991 // we're not possibly in the last week -must be ywoy
2997 if((internalGet(UCAL_MONTH
)==0) &&
2998 (woy
>= getLeastMaximum(UCAL_WEEK_OF_YEAR
))) {
2999 return yearWoy
+1; // month 0, late woy = in the next year
3001 //if(nextJan1InPrevYear) {
3002 if(internalGet(UCAL_MONTH
)==0) {
3010 //(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow */ ) {
3011 //within 1st week and in this month..
3016 default: // assume the year is appropriate
3021 #if defined (U_DEBUG_CAL)
3022 fprintf(stderr
, "%s:%d - forgot a return on field %s\n", __FILE__
, __LINE__
, fldName(bestField
));
3028 int32_t Calendar::handleGetMonthLength(int32_t extendedYear
, int32_t month
) const
3030 return handleComputeMonthStart(extendedYear
, month
+1, TRUE
) -
3031 handleComputeMonthStart(extendedYear
, month
, TRUE
);
3034 int32_t Calendar::handleGetYearLength(int32_t eyear
) const {
3035 return handleComputeMonthStart(eyear
+1, 0, FALSE
) -
3036 handleComputeMonthStart(eyear
, 0, FALSE
);
3040 Calendar::getActualMaximum(UCalendarDateFields field
, UErrorCode
& status
) const
3046 if(U_FAILURE(status
)) return 0;
3047 Calendar
*cal
= clone();
3048 if(!cal
) { status
= U_MEMORY_ALLOCATION_ERROR
; return 0; }
3049 cal
->prepareGetActual(field
,FALSE
,status
);
3050 result
= handleGetMonthLength(cal
->get(UCAL_EXTENDED_YEAR
, status
), cal
->get(UCAL_MONTH
, status
));
3055 case UCAL_DAY_OF_YEAR
:
3057 if(U_FAILURE(status
)) return 0;
3058 Calendar
*cal
= clone();
3059 if(!cal
) { status
= U_MEMORY_ALLOCATION_ERROR
; return 0; }
3060 cal
->prepareGetActual(field
,FALSE
,status
);
3061 result
= handleGetYearLength(cal
->get(UCAL_EXTENDED_YEAR
, status
));
3066 case UCAL_DAY_OF_WEEK
:
3069 case UCAL_HOUR_OF_DAY
:
3072 case UCAL_MILLISECOND
:
3073 case UCAL_ZONE_OFFSET
:
3074 case UCAL_DST_OFFSET
:
3075 case UCAL_DOW_LOCAL
:
3076 case UCAL_JULIAN_DAY
:
3077 case UCAL_MILLISECONDS_IN_DAY
:
3078 // These fields all have fixed minima/maxima
3079 result
= getMaximum(field
);
3083 // For all other fields, do it the hard way....
3084 result
= getActualHelper(field
, getLeastMaximum(field
), getMaximum(field
),status
);
3092 * Prepare this calendar for computing the actual minimum or maximum.
3093 * This method modifies this calendar's fields; it is called on a
3094 * temporary calendar.
3096 * <p>Rationale: The semantics of getActualXxx() is to return the
3097 * maximum or minimum value that the given field can take, taking into
3098 * account other relevant fields. In general these other fields are
3099 * larger fields. For example, when computing the actual maximum
3100 * DATE, the current value of DATE itself is ignored,
3101 * as is the value of any field smaller.
3103 * <p>The time fields all have fixed minima and maxima, so we don't
3104 * need to worry about them. This also lets us set the
3105 * MILLISECONDS_IN_DAY to zero to erase any effects the time fields
3106 * might have when computing date fields.
3108 * <p>DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and
3109 * WEEK_OF_YEAR fields to ensure that they are computed correctly.
3112 void Calendar::prepareGetActual(UCalendarDateFields field
, UBool isMinimum
, UErrorCode
&status
)
3114 set(UCAL_MILLISECONDS_IN_DAY
, 0);
3118 case UCAL_EXTENDED_YEAR
:
3119 set(UCAL_DAY_OF_YEAR
, getGreatestMinimum(UCAL_DAY_OF_YEAR
));
3123 set(UCAL_WEEK_OF_YEAR
, getGreatestMinimum(UCAL_WEEK_OF_YEAR
));
3126 set(UCAL_DATE
, getGreatestMinimum(UCAL_DATE
));
3129 case UCAL_DAY_OF_WEEK_IN_MONTH
:
3130 // For dowim, the maximum occurs for the DOW of the first of the
3133 set(UCAL_DAY_OF_WEEK
, get(UCAL_DAY_OF_WEEK
, status
)); // Make this user set
3136 case UCAL_WEEK_OF_MONTH
:
3137 case UCAL_WEEK_OF_YEAR
:
3138 // If we're counting weeks, set the day of the week to either the
3139 // first or last localized DOW. We know the last week of a month
3140 // or year will contain the first day of the week, and that the
3141 // first week will contain the last DOW.
3143 int32_t dow
= fFirstDayOfWeek
;
3145 dow
= (dow
+ 6) % 7; // set to last DOW
3146 if (dow
< UCAL_SUNDAY
) {
3150 #if defined (U_DEBUG_CAL)
3151 fprintf(stderr
, "prepareGetActualHelper(WOM/WOY) - dow=%d\n", dow
);
3153 set(UCAL_DAY_OF_WEEK
, dow
);
3160 // Do this last to give it the newest time stamp
3161 set(field
, getGreatestMinimum(field
));
3164 int32_t Calendar::getActualHelper(UCalendarDateFields field
, int32_t startValue
, int32_t endValue
, UErrorCode
&status
) const
3166 #if defined (U_DEBUG_CAL)
3167 fprintf(stderr
, "getActualHelper(%d,%d .. %d, %s)\n", field
, startValue
, endValue
, u_errorName(status
));
3169 if (startValue
== endValue
) {
3170 // if we know that the maximum value is always the same, just return it
3174 int32_t delta
= (endValue
> startValue
) ? 1 : -1;
3176 // clone the calendar so we don't mess with the real one, and set it to
3177 // accept anything for the field values
3178 if(U_FAILURE(status
)) return startValue
;
3179 Calendar
*work
= clone();
3180 if(!work
) { status
= U_MEMORY_ALLOCATION_ERROR
; return startValue
; }
3181 work
->setLenient(TRUE
);
3182 work
->prepareGetActual(field
, delta
< 0, status
);
3184 // now try each value from the start to the end one by one until
3185 // we get a value that normalizes to another value. The last value that
3186 // normalizes to itself is the actual maximum for the current date
3187 work
->set(field
, startValue
);
3189 // prepareGetActual sets the first day of week in the same week with
3190 // the first day of a month. Unlike WEEK_OF_YEAR, week number for the
3191 // week which contains days from both previous and current month is
3192 // not unique. For example, last several days in the previous month
3193 // is week 5, and the rest of week is week 1.
3194 int32_t result
= startValue
;
3195 if ((work
->get(field
, status
) != startValue
3196 && field
!= UCAL_WEEK_OF_MONTH
&& delta
> 0 ) || U_FAILURE(status
)) {
3197 #if defined (U_DEBUG_CAL)
3198 fprintf(stderr
, "getActualHelper(fld %d) - got %d (not %d) - %s\n", field
, work
->get(field
,status
), startValue
, u_errorName(status
));
3202 startValue
+= delta
;
3203 work
->add(field
, delta
, status
);
3204 if (work
->get(field
, status
) != startValue
|| U_FAILURE(status
)) {
3205 #if defined (U_DEBUG_CAL)
3206 fprintf(stderr
, "getActualHelper(fld %d) - got %d (not %d), BREAK - %s\n", field
, work
->get(field
,status
), startValue
, u_errorName(status
));
3210 result
= startValue
;
3211 } while (startValue
!= endValue
);
3214 #if defined (U_DEBUG_CAL)
3215 fprintf(stderr
, "getActualHelper(%d) = %d\n", field
, result
);
3223 // -------------------------------------
3226 Calendar::setWeekData(const Locale
& desiredLocale
, const char *type
, UErrorCode
& status
)
3228 // Read the week count data from the resource bundle. This should
3231 // DateTimeElements:intvector {
3232 // 1, // first day of week
3233 // 1 // min days in week
3235 // Both have a range of 1..7
3238 if (U_FAILURE(status
)) return;
3240 fFirstDayOfWeek
= UCAL_SUNDAY
;
3241 fMinimalDaysInFirstWeek
= 1;
3242 fWeekendOnset
= UCAL_SATURDAY
;
3243 fWeekendOnsetMillis
= 0;
3244 fWeekendCease
= UCAL_SUNDAY
;
3245 fWeekendCeaseMillis
= 86400000; // 24*60*60*1000
3247 // Since week and weekend data is territory based instead of language based,
3248 // we may need to tweak the locale that we are using to try to get the appropriate
3249 // values, using the following logic:
3250 // 1). If the locale has a language but no territory, use the territory as defined by
3251 // the likely subtags.
3252 // 2). If the locale has a script designation then we ignore it,
3253 // then remove it ( i.e. "en_Latn_US" becomes "en_US" )
3255 char minLocaleID
[ULOC_FULLNAME_CAPACITY
] = { 0 };
3256 UErrorCode myStatus
= U_ZERO_ERROR
;
3258 uloc_minimizeSubtags(desiredLocale
.getName(),minLocaleID
,ULOC_FULLNAME_CAPACITY
,&myStatus
);
3259 Locale min
= Locale::createFromName(minLocaleID
);
3261 if ( uprv_strlen(desiredLocale
.getCountry()) == 0 ||
3262 uprv_strlen(desiredLocale
.getScript()) > 0 && uprv_strlen(min
.getScript()) == 0 ) {
3263 char maxLocaleID
[ULOC_FULLNAME_CAPACITY
] = { 0 };
3264 myStatus
= U_ZERO_ERROR
;
3265 uloc_addLikelySubtags(desiredLocale
.getName(),maxLocaleID
,ULOC_FULLNAME_CAPACITY
,&myStatus
);
3266 Locale max
= Locale::createFromName(maxLocaleID
);
3267 useLocale
= Locale(max
.getLanguage(),max
.getCountry());
3269 useLocale
= Locale(desiredLocale
);
3272 CalendarData
calData(useLocale
, type
, status
);
3273 // If the resource data doesn't seem to be present at all, then use last-resort
3275 UResourceBundle
*dateTimeElements
= calData
.getByKey(gDateTimeElements
, status
);
3277 if (U_FAILURE(status
)) {
3278 #if defined (U_DEBUG_CALDATA)
3279 fprintf(stderr
, " Failure loading dateTimeElements = %s\n", u_errorName(status
));
3281 status
= U_USING_FALLBACK_WARNING
;
3283 U_LOCALE_BASED(locBased
, *this);
3284 locBased
.setLocaleIDs(ures_getLocaleByType(dateTimeElements
, ULOC_VALID_LOCALE
, &status
),
3285 ures_getLocaleByType(dateTimeElements
, ULOC_ACTUAL_LOCALE
, &status
));
3286 if (U_SUCCESS(status
)) {
3287 #if defined (U_DEBUG_CAL)
3288 fprintf(stderr
, " Valid=%s, Actual=%s\n", validLocale
, actualLocale
);
3291 const int32_t *dateTimeElementsArr
= ures_getIntVector(dateTimeElements
, &arrLen
, &status
);
3293 if(U_SUCCESS(status
) && arrLen
== 2
3294 && 1 <= dateTimeElementsArr
[0] && dateTimeElementsArr
[0] <= 7
3295 && 1 <= dateTimeElementsArr
[1] && dateTimeElementsArr
[1] <= 7)
3297 fFirstDayOfWeek
= (UCalendarDaysOfWeek
)dateTimeElementsArr
[0];
3298 fMinimalDaysInFirstWeek
= (uint8_t)dateTimeElementsArr
[1];
3301 status
= U_INVALID_FORMAT_ERROR
;
3305 // do NOT close dateTimeElements
3307 if (U_SUCCESS(status
)) {
3308 UResourceBundle
*weekend
= calData
.getByKey(gWeekend
, status
);
3309 if (U_FAILURE(status
)) {
3310 status
= U_USING_FALLBACK_WARNING
;
3313 const int32_t *weekendArr
= ures_getIntVector(weekend
, &arrLen
, &status
);
3314 if(U_SUCCESS(status
) && arrLen
>= 4
3315 && 1 <= weekendArr
[0] && weekendArr
[0] <= 7
3316 && 1 <= weekendArr
[2] && weekendArr
[2] <= 7)
3318 fWeekendOnset
= (UCalendarDaysOfWeek
)weekendArr
[0];
3319 fWeekendOnsetMillis
= weekendArr
[1];
3320 fWeekendCease
= (UCalendarDaysOfWeek
)weekendArr
[2];
3321 fWeekendCeaseMillis
= weekendArr
[3];
3324 status
= U_INVALID_FORMAT_ERROR
;
3331 * Recompute the time and update the status fields isTimeSet
3332 * and areFieldsSet. Callers should check isTimeSet and only
3333 * call this method if isTimeSet is false.
3336 Calendar::updateTime(UErrorCode
& status
)
3338 computeTime(status
);
3339 if(U_FAILURE(status
))
3342 // If we are lenient, we need to recompute the fields to normalize
3343 // the values. Also, if we haven't set all the fields yet (i.e.,
3344 // in a newly-created object), we need to fill in the fields. [LIU]
3345 if (isLenient() || ! fAreAllFieldsSet
)
3346 fAreFieldsSet
= FALSE
;
3349 fAreFieldsVirtuallySet
= FALSE
;
3353 Calendar::getLocale(ULocDataLocaleType type
, UErrorCode
& status
) const {
3354 U_LOCALE_BASED(locBased
, *this);
3355 return locBased
.getLocale(type
, status
);
3359 Calendar::getLocaleID(ULocDataLocaleType type
, UErrorCode
& status
) const {
3360 U_LOCALE_BASED(locBased
, *this);
3361 return locBased
.getLocaleID(type
, status
);
3364 // Deprecated function. This doesn't need to be inline.
3366 Calendar::internalSet(EDateFields field
, int32_t value
)
3368 internalSet((UCalendarDateFields
) field
, value
);
3373 #endif /* #if !UCONFIG_NO_FORMATTING */