2 *******************************************************************************
3 * Copyright (C) 1997-2013, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
9 * Modification History:
11 * Date Name Description
12 * 02/03/97 clhuang Creation.
13 * 04/22/97 aliu Cleaned up, fixed memory leak, made
14 * setWeekCountData() more robust.
15 * Moved platform code to TPlatformUtilities.
16 * 05/01/97 aliu Made equals(), before(), after() arguments const.
17 * 05/20/97 aliu Changed logic of when to compute fields and time
19 * 08/12/97 aliu Added equivalentTo. Misc other fixes.
20 * 07/28/98 stephen Sync up with JDK 1.2
21 * 09/02/98 stephen Sync with JDK 1.2 8/31 build (getActualMin/Max)
22 * 03/17/99 stephen Changed adoptTimeZone() - now fAreFieldsSet is
23 * set to FALSE to force update of time.
24 *******************************************************************************
27 #include "utypeinfo.h" // for 'typeid' to work
29 #include "unicode/utypes.h"
31 #if !UCONFIG_NO_FORMATTING
33 #include "unicode/gregocal.h"
34 #include "unicode/basictz.h"
35 #include "unicode/simpletz.h"
36 #include "unicode/rbtz.h"
37 #include "unicode/vtzone.h"
45 #include "indiancal.h"
50 #include "unicode/calendar.h"
61 #if !UCONFIG_NO_SERVICE
62 static icu::ICULocaleService
* gService
= NULL
;
65 // INTERNAL - for cleanup
68 static UBool
calendar_cleanup(void) {
69 #if !UCONFIG_NO_SERVICE
79 // ------------------------------------------
83 //-------------------------------------------
84 //#define U_DEBUG_CALSVC 1
87 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
90 * fldName was removed as a duplicate implementation.
91 * use udbg_ services instead,
92 * which depend on include files and library from ../tools/toolutil, the following circular link:
93 * CPPFLAGS+=-I$(top_srcdir)/tools/toolutil
94 * LIBS+=$(LIBICUTOOLUTIL)
100 * convert a UCalendarDateFields into a string - for debugging
101 * @param f field enum
102 * @return static string to the field name
106 const char* fldName(UCalendarDateFields f
) {
107 return udbg_enumName(UDBG_UCalendarDateFields
, (int32_t)f
);
111 // from CalendarTest::calToStr - but doesn't modify contents.
112 void ucal_dump(const Calendar
&cal
) {
116 void Calendar::dump() const {
118 fprintf(stderr
, "@calendar=%s, timeset=%c, fieldset=%c, allfields=%c, virtualset=%c, t=%.2f",
119 getType(), fIsTimeSet
?'y':'n', fAreFieldsSet
?'y':'n', fAreAllFieldsSet
?'y':'n',
120 fAreFieldsVirtuallySet
?'y':'n',
123 // can add more things here: DST, zone, etc.
124 fprintf(stderr
, "\n");
125 for(i
= 0;i
<UCAL_FIELD_COUNT
;i
++) {
127 const char *f
= fldName((UCalendarDateFields
)i
);
128 fprintf(stderr
, " %25s: %-11ld", f
, fFields
[i
]);
129 if(fStamp
[i
] == kUnset
) {
130 fprintf(stderr
, " (unset) ");
131 } else if(fStamp
[i
] == kInternallySet
) {
132 fprintf(stderr
, " (internally set) ");
133 //} else if(fStamp[i] == kInternalDefault) {
134 // fprintf(stderr, " (internal default) ");
136 fprintf(stderr
, " %%%d ", fStamp
[i
]);
138 fprintf(stderr
, "\n");
143 U_CFUNC
void ucal_dump(UCalendar
* cal
) {
144 ucal_dump( *((Calendar
*)cal
) );
150 /* Max value for stamp allowable before recalculation */
151 #define STAMP_MAX 10000
153 static const char * const gCalTypes
[] = {
166 "ethiopic-amete-alem",
172 // Must be in the order of gCalTypes above
173 typedef enum ECalType
{
174 CALTYPE_UNKNOWN
= -1,
175 CALTYPE_GREGORIAN
= 0,
180 CALTYPE_ISLAMIC_CIVIL
,
187 CALTYPE_ETHIOPIC_AMETE_ALEM
,
194 static ECalType
getCalendarType(const char *s
) {
195 for (int i
= 0; gCalTypes
[i
] != NULL
; i
++) {
196 if (uprv_stricmp(s
, gCalTypes
[i
]) == 0) {
200 return CALTYPE_UNKNOWN
;
203 static UBool
isStandardSupportedKeyword(const char *keyword
, UErrorCode
& status
) {
204 if(U_FAILURE(status
)) {
207 ECalType calType
= getCalendarType(keyword
);
208 return (calType
!= CALTYPE_UNKNOWN
);
211 static void getCalendarKeyword(const UnicodeString
&id
, char *targetBuffer
, int32_t targetBufferSize
) {
212 UnicodeString calendarKeyword
= UNICODE_STRING_SIMPLE("calendar=");
213 int32_t calKeyLen
= calendarKeyword
.length();
216 int32_t keywordIdx
= id
.indexOf((UChar
)0x003D); /* '=' */
217 if (id
[0] == 0x40/*'@'*/
218 && id
.compareBetween(1, keywordIdx
+1, calendarKeyword
, 0, calKeyLen
) == 0)
220 keyLen
= id
.extract(keywordIdx
+1, id
.length(), targetBuffer
, targetBufferSize
, US_INV
);
222 targetBuffer
[keyLen
] = 0;
225 static ECalType
getCalendarTypeForLocale(const char *locid
) {
226 UErrorCode status
= U_ZERO_ERROR
;
227 ECalType calType
= CALTYPE_UNKNOWN
;
229 //TODO: ULOC_FULL_NAME is out of date and too small..
230 char canonicalName
[256];
232 // canonicalize, so grandfathered variant will be transformed to keywords
233 // e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese
234 int32_t canonicalLen
= uloc_canonicalize(locid
, canonicalName
, sizeof(canonicalName
) - 1, &status
);
235 if (U_FAILURE(status
)) {
236 return CALTYPE_GREGORIAN
;
238 canonicalName
[canonicalLen
] = 0; // terminate
241 int32_t calTypeBufLen
;
243 calTypeBufLen
= uloc_getKeywordValue(canonicalName
, "calendar", calTypeBuf
, sizeof(calTypeBuf
) - 1, &status
);
244 if (U_SUCCESS(status
)) {
245 calTypeBuf
[calTypeBufLen
] = 0;
246 calType
= getCalendarType(calTypeBuf
);
247 if (calType
!= CALTYPE_UNKNOWN
) {
251 status
= U_ZERO_ERROR
;
253 // when calendar keyword is not available or not supported, read supplementalData
254 // to get the default calendar type for the locale's region
255 char region
[ULOC_COUNTRY_CAPACITY
];
256 int32_t regionLen
= 0;
257 regionLen
= uloc_getCountry(canonicalName
, region
, sizeof(region
) - 1, &status
);
258 if (regionLen
== 0) {
260 uloc_addLikelySubtags(locid
, fullLoc
, sizeof(fullLoc
) - 1, &status
);
261 regionLen
= uloc_getCountry(fullLoc
, region
, sizeof(region
) - 1, &status
);
263 if (U_FAILURE(status
)) {
264 return CALTYPE_GREGORIAN
;
266 region
[regionLen
] = 0;
268 // Read preferred calendar values from supplementalData calendarPreference
269 UResourceBundle
*rb
= ures_openDirect(NULL
, "supplementalData", &status
);
270 ures_getByKey(rb
, "calendarPreferenceData", rb
, &status
);
271 UResourceBundle
*order
= ures_getByKey(rb
, region
, NULL
, &status
);
272 if (status
== U_MISSING_RESOURCE_ERROR
&& rb
!= NULL
) {
273 status
= U_ZERO_ERROR
;
274 order
= ures_getByKey(rb
, "001", NULL
, &status
);
278 if (U_SUCCESS(status
) && order
!= NULL
) {
279 // the first calender type is the default for the region
281 const UChar
*uCalType
= ures_getStringByIndex(order
, 0, &len
, &status
);
282 if (len
< (int32_t)sizeof(calTypeBuf
)) {
283 u_UCharsToChars(uCalType
, calTypeBuf
, len
);
284 *(calTypeBuf
+ len
) = 0; // terminate;
285 calType
= getCalendarType(calTypeBuf
);
292 if (calType
== CALTYPE_UNKNOWN
) {
294 calType
= CALTYPE_GREGORIAN
;
299 static Calendar
*createStandardCalendar(ECalType calType
, const Locale
&loc
, UErrorCode
& status
) {
300 Calendar
*cal
= NULL
;
303 case CALTYPE_GREGORIAN
:
304 cal
= new GregorianCalendar(loc
, status
);
306 case CALTYPE_JAPANESE
:
307 cal
= new JapaneseCalendar(loc
, status
);
309 case CALTYPE_BUDDHIST
:
310 cal
= new BuddhistCalendar(loc
, status
);
313 cal
= new TaiwanCalendar(loc
, status
);
315 case CALTYPE_PERSIAN
:
316 cal
= new PersianCalendar(loc
, status
);
318 case CALTYPE_ISLAMIC_CIVIL
:
319 cal
= new IslamicCalendar(loc
, status
, IslamicCalendar::CIVIL
);
321 case CALTYPE_ISLAMIC
:
322 cal
= new IslamicCalendar(loc
, status
, IslamicCalendar::ASTRONOMICAL
);
325 cal
= new HebrewCalendar(loc
, status
);
327 case CALTYPE_CHINESE
:
328 cal
= new ChineseCalendar(loc
, status
);
331 cal
= new IndianCalendar(loc
, status
);
334 cal
= new CopticCalendar(loc
, status
);
336 case CALTYPE_ETHIOPIC
:
337 cal
= new EthiopicCalendar(loc
, status
, EthiopicCalendar::AMETE_MIHRET_ERA
);
339 case CALTYPE_ETHIOPIC_AMETE_ALEM
:
340 cal
= new EthiopicCalendar(loc
, status
, EthiopicCalendar::AMETE_ALEM_ERA
);
342 case CALTYPE_ISO8601
:
343 cal
= new GregorianCalendar(loc
, status
);
344 cal
->setFirstDayOfWeek(UCAL_MONDAY
);
345 cal
->setMinimalDaysInFirstWeek(4);
348 cal
= new DangiCalendar(loc
, status
);
351 status
= U_UNSUPPORTED_ERROR
;
357 #if !UCONFIG_NO_SERVICE
359 // -------------------------------------
362 * a Calendar Factory which creates the "basic" calendar types, that is, those
365 class BasicCalendarFactory
: public LocaleKeyFactory
{
368 * @param calendarType static const string (caller owns storage - will be aliased) to calendar type
370 BasicCalendarFactory()
371 : LocaleKeyFactory(LocaleKeyFactory::INVISIBLE
) { }
373 virtual ~BasicCalendarFactory();
376 //virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const {
377 // if(U_FAILURE(status)) {
380 // char keyword[ULOC_FULLNAME_CAPACITY];
381 // getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword));
382 // return isStandardSupportedKeyword(keyword, status);
385 virtual void updateVisibleIDs(Hashtable
& result
, UErrorCode
& status
) const
387 if (U_SUCCESS(status
)) {
388 for(int32_t i
=0;gCalTypes
[i
] != NULL
;i
++) {
389 UnicodeString
id((UChar
)0x40); /* '@' a variant character */
390 id
.append(UNICODE_STRING_SIMPLE("calendar="));
391 id
.append(UnicodeString(gCalTypes
[i
], -1, US_INV
));
392 result
.put(id
, (void*)this, status
);
397 virtual UObject
* create(const ICUServiceKey
& key
, const ICUService
* /*service*/, UErrorCode
& status
) const {
398 #ifdef U_DEBUG_CALSVC
399 if(dynamic_cast<const LocaleKey
*>(&key
) == NULL
) {
400 fprintf(stderr
, "::create - not a LocaleKey!\n");
403 const LocaleKey
& lkey
= (LocaleKey
&)key
;
404 Locale curLoc
; // current locale
405 Locale canLoc
; // Canonical locale
407 lkey
.currentLocale(curLoc
);
408 lkey
.canonicalLocale(canLoc
);
410 char keyword
[ULOC_FULLNAME_CAPACITY
];
414 getCalendarKeyword(str
, keyword
, (int32_t) sizeof(keyword
));
416 #ifdef U_DEBUG_CALSVC
417 fprintf(stderr
, "BasicCalendarFactory::create() - cur %s, can %s\n", (const char*)curLoc
.getName(), (const char*)canLoc
.getName());
420 if(!isStandardSupportedKeyword(keyword
,status
)) { // Do we handle this type?
421 #ifdef U_DEBUG_CALSVC
423 fprintf(stderr
, "BasicCalendarFactory - not handling %s.[%s]\n", (const char*) curLoc
.getName(), tmp
);
428 return createStandardCalendar(getCalendarType(keyword
), canLoc
, status
);
432 BasicCalendarFactory::~BasicCalendarFactory() {}
435 * A factory which looks up the DefaultCalendar resource to determine which class of calendar to use
438 class DefaultCalendarFactory
: public ICUResourceBundleFactory
{
440 DefaultCalendarFactory() : ICUResourceBundleFactory() { }
441 virtual ~DefaultCalendarFactory();
443 virtual UObject
* create(const ICUServiceKey
& key
, const ICUService
* /*service*/, UErrorCode
& status
) const {
445 LocaleKey
&lkey
= (LocaleKey
&)key
;
447 lkey
.currentLocale(loc
);
449 UnicodeString
*ret
= new UnicodeString();
451 status
= U_MEMORY_ALLOCATION_ERROR
;
453 ret
->append((UChar
)0x40); // '@' is a variant character
454 ret
->append(UNICODE_STRING("calendar=", 9));
455 ret
->append(UnicodeString(gCalTypes
[getCalendarTypeForLocale(loc
.getName())], -1, US_INV
));
461 DefaultCalendarFactory::~DefaultCalendarFactory() {}
463 // -------------------------------------
464 class CalendarService
: public ICULocaleService
{
467 : ICULocaleService(UNICODE_STRING_SIMPLE("Calendar"))
469 UErrorCode status
= U_ZERO_ERROR
;
470 registerFactory(new DefaultCalendarFactory(), status
);
473 virtual ~CalendarService();
475 virtual UObject
* cloneInstance(UObject
* instance
) const {
476 UnicodeString
*s
= dynamic_cast<UnicodeString
*>(instance
);
480 #ifdef U_DEBUG_CALSVC_F
481 UErrorCode status2
= U_ZERO_ERROR
;
482 fprintf(stderr
, "Cloning a %s calendar with tz=%ld\n", ((Calendar
*)instance
)->getType(), ((Calendar
*)instance
)->get(UCAL_ZONE_OFFSET
, status2
));
484 return ((Calendar
*)instance
)->clone();
488 virtual UObject
* handleDefault(const ICUServiceKey
& key
, UnicodeString
* /*actualID*/, UErrorCode
& status
) const {
489 LocaleKey
& lkey
= (LocaleKey
&)key
;
490 //int32_t kind = lkey.kind();
493 lkey
.canonicalLocale(loc
);
495 #ifdef U_DEBUG_CALSVC
497 lkey
.currentLocale(loc2
);
498 fprintf(stderr
, "CalSvc:handleDefault for currentLoc %s, canloc %s\n", (const char*)loc
.getName(), (const char*)loc2
.getName());
500 Calendar
*nc
= new GregorianCalendar(loc
, status
);
502 #ifdef U_DEBUG_CALSVC
503 UErrorCode status2
= U_ZERO_ERROR
;
504 fprintf(stderr
, "New default calendar has tz=%d\n", ((Calendar
*)nc
)->get(UCAL_ZONE_OFFSET
, status2
));
509 virtual UBool
isDefault() const {
510 return countFactories() == 1;
514 CalendarService::~CalendarService() {}
516 // -------------------------------------
519 isCalendarServiceUsed() {
521 UMTX_CHECK(NULL
, gService
!= NULL
, retVal
);
525 // -------------------------------------
527 static ICULocaleService
*
528 getCalendarService(UErrorCode
&status
)
531 UMTX_CHECK(NULL
, (UBool
)(gService
== NULL
), needInit
);
533 #ifdef U_DEBUG_CALSVC
534 fprintf(stderr
, "Spinning up Calendar Service\n");
536 ICULocaleService
* newservice
= new CalendarService();
537 if (newservice
== NULL
) {
538 status
= U_MEMORY_ALLOCATION_ERROR
;
541 #ifdef U_DEBUG_CALSVC
542 fprintf(stderr
, "Registering classes..\n");
545 // Register all basic instances.
546 newservice
->registerFactory(new BasicCalendarFactory(),status
);
548 #ifdef U_DEBUG_CALSVC
549 fprintf(stderr
, "Done..\n");
552 if(U_FAILURE(status
)) {
553 #ifdef U_DEBUG_CALSVC
554 fprintf(stderr
, "err (%s) registering classes, deleting service.....\n", u_errorName(status
));
562 if (gService
== NULL
) {
563 gService
= newservice
;
571 // we won the contention - we can register the cleanup.
572 ucln_i18n_registerCleanup(UCLN_I18N_CALENDAR
, calendar_cleanup
);
578 URegistryKey
Calendar::registerFactory(ICUServiceFactory
* toAdopt
, UErrorCode
& status
)
580 return getCalendarService(status
)->registerFactory(toAdopt
, status
);
583 UBool
Calendar::unregister(URegistryKey key
, UErrorCode
& status
) {
584 return getCalendarService(status
)->unregister(key
, status
);
586 #endif /* UCONFIG_NO_SERVICE */
588 // -------------------------------------
590 static const int32_t kCalendarLimits
[UCAL_FIELD_COUNT
][4] = {
591 // Minimum Greatest min Least max Greatest max
592 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // ERA
593 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR
594 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // MONTH
595 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_YEAR
596 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_MONTH
597 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_MONTH
598 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_YEAR
599 { 1, 1, 7, 7 }, // DAY_OF_WEEK
600 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_WEEK_IN_MONTH
601 { 0, 0, 1, 1 }, // AM_PM
602 { 0, 0, 11, 11 }, // HOUR
603 { 0, 0, 23, 23 }, // HOUR_OF_DAY
604 { 0, 0, 59, 59 }, // MINUTE
605 { 0, 0, 59, 59 }, // SECOND
606 { 0, 0, 999, 999 }, // MILLISECOND
607 {-12*kOneHour
, -12*kOneHour
, 12*kOneHour
, 15*kOneHour
}, // ZONE_OFFSET
608 { 0, 0, 1*kOneHour
, 1*kOneHour
}, // DST_OFFSET
609 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR_WOY
610 { 1, 1, 7, 7 }, // DOW_LOCAL
611 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // EXTENDED_YEAR
612 { -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY
613 { 0, 0, 24*kOneHour
-1, 24*kOneHour
-1 }, // MILLISECONDS_IN_DAY
614 { 0, 0, 1, 1 }, // IS_LEAP_MONTH
617 // Resource bundle tags read by this class
618 static const char gMonthNames
[] = "monthNames";
620 // Data flow in Calendar
621 // ---------------------
623 // The current time is represented in two ways by Calendar: as UTC
624 // milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local
625 // fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the
626 // millis from the fields, and vice versa. The data needed to do this
627 // conversion is encapsulated by a TimeZone object owned by the Calendar.
628 // The data provided by the TimeZone object may also be overridden if the
629 // user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class
630 // keeps track of what information was most recently set by the caller, and
631 // uses that to compute any other information as needed.
633 // If the user sets the fields using set(), the data flow is as follows.
634 // This is implemented by the Calendar subclass's computeTime() method.
635 // During this process, certain fields may be ignored. The disambiguation
636 // algorithm for resolving which fields to pay attention to is described
639 // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
641 // | Using Calendar-specific algorithm
643 // local standard millis
645 // | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET
647 // UTC millis (in time data member)
649 // If the user sets the UTC millis using setTime(), the data flow is as
650 // follows. This is implemented by the Calendar subclass's computeFields()
653 // UTC millis (in time data member)
655 // | Using TimeZone getOffset()
657 // local standard millis
659 // | Using Calendar-specific algorithm
661 // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
663 // In general, a round trip from fields, through local and UTC millis, and
664 // back out to fields is made when necessary. This is implemented by the
665 // complete() method. Resolving a partial set of fields into a UTC millis
666 // value allows all remaining fields to be generated from that value. If
667 // the Calendar is lenient, the fields are also renormalized to standard
668 // ranges when they are regenerated.
670 // -------------------------------------
672 Calendar::Calendar(UErrorCode
& success
)
675 fAreFieldsSet(FALSE
),
676 fAreAllFieldsSet(FALSE
),
677 fAreFieldsVirtuallySet(FALSE
),
678 fNextStamp((int32_t)kMinimumUserStamp
),
682 fRepeatedWallTime(UCAL_WALLTIME_LAST
),
683 fSkippedWallTime(UCAL_WALLTIME_LAST
)
686 fZone
= TimeZone::createDefault();
688 success
= U_MEMORY_ALLOCATION_ERROR
;
690 setWeekData(Locale::getDefault(), NULL
, success
);
693 // -------------------------------------
695 Calendar::Calendar(TimeZone
* zone
, const Locale
& aLocale
, UErrorCode
& success
)
698 fAreFieldsSet(FALSE
),
699 fAreAllFieldsSet(FALSE
),
700 fAreFieldsVirtuallySet(FALSE
),
701 fNextStamp((int32_t)kMinimumUserStamp
),
705 fRepeatedWallTime(UCAL_WALLTIME_LAST
),
706 fSkippedWallTime(UCAL_WALLTIME_LAST
)
709 #if defined (U_DEBUG_CAL)
710 fprintf(stderr
, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n",
713 success
= U_ILLEGAL_ARGUMENT_ERROR
;
720 setWeekData(aLocale
, NULL
, success
);
723 // -------------------------------------
725 Calendar::Calendar(const TimeZone
& zone
, const Locale
& aLocale
, UErrorCode
& success
)
728 fAreFieldsSet(FALSE
),
729 fAreAllFieldsSet(FALSE
),
730 fAreFieldsVirtuallySet(FALSE
),
731 fNextStamp((int32_t)kMinimumUserStamp
),
735 fRepeatedWallTime(UCAL_WALLTIME_LAST
),
736 fSkippedWallTime(UCAL_WALLTIME_LAST
)
739 fZone
= zone
.clone();
741 success
= U_MEMORY_ALLOCATION_ERROR
;
743 setWeekData(aLocale
, NULL
, success
);
746 // -------------------------------------
748 Calendar::~Calendar()
753 // -------------------------------------
755 Calendar::Calendar(const Calendar
&source
)
762 // -------------------------------------
765 Calendar::operator=(const Calendar
&right
)
767 if (this != &right
) {
768 uprv_arrayCopy(right
.fFields
, fFields
, UCAL_FIELD_COUNT
);
769 uprv_arrayCopy(right
.fIsSet
, fIsSet
, UCAL_FIELD_COUNT
);
770 uprv_arrayCopy(right
.fStamp
, fStamp
, UCAL_FIELD_COUNT
);
772 fIsTimeSet
= right
.fIsTimeSet
;
773 fAreAllFieldsSet
= right
.fAreAllFieldsSet
;
774 fAreFieldsSet
= right
.fAreFieldsSet
;
775 fAreFieldsVirtuallySet
= right
.fAreFieldsVirtuallySet
;
776 fLenient
= right
.fLenient
;
777 fRepeatedWallTime
= right
.fRepeatedWallTime
;
778 fSkippedWallTime
= right
.fSkippedWallTime
;
782 if (right
.fZone
!= NULL
) {
783 fZone
= right
.fZone
->clone();
785 fFirstDayOfWeek
= right
.fFirstDayOfWeek
;
786 fMinimalDaysInFirstWeek
= right
.fMinimalDaysInFirstWeek
;
787 fWeekendOnset
= right
.fWeekendOnset
;
788 fWeekendOnsetMillis
= right
.fWeekendOnsetMillis
;
789 fWeekendCease
= right
.fWeekendCease
;
790 fWeekendCeaseMillis
= right
.fWeekendCeaseMillis
;
791 fNextStamp
= right
.fNextStamp
;
792 uprv_strcpy(validLocale
, right
.validLocale
);
793 uprv_strcpy(actualLocale
, right
.actualLocale
);
799 // -------------------------------------
802 Calendar::createInstance(UErrorCode
& success
)
804 return createInstance(TimeZone::createDefault(), Locale::getDefault(), success
);
807 // -------------------------------------
810 Calendar::createInstance(const TimeZone
& zone
, UErrorCode
& success
)
812 return createInstance(zone
, Locale::getDefault(), success
);
815 // -------------------------------------
818 Calendar::createInstance(const Locale
& aLocale
, UErrorCode
& success
)
820 return createInstance(TimeZone::createDefault(), aLocale
, success
);
823 // ------------------------------------- Adopting
825 // Note: this is the bottleneck that actually calls the service routines.
828 Calendar::createInstance(TimeZone
* zone
, const Locale
& aLocale
, UErrorCode
& success
)
830 if (U_FAILURE(success
)) {
837 #if !UCONFIG_NO_SERVICE
838 if (isCalendarServiceUsed()) {
839 u
= getCalendarService(success
)->get(aLocale
, LocaleKey::KIND_ANY
, &actualLoc
, success
);
844 u
= createStandardCalendar(getCalendarTypeForLocale(aLocale
.getName()), aLocale
, success
);
848 if(U_FAILURE(success
) || !u
) {
850 if(U_SUCCESS(success
)) { // Propagate some kind of err
851 success
= U_INTERNAL_PROGRAM_ERROR
;
856 #if !UCONFIG_NO_SERVICE
857 const UnicodeString
* str
= dynamic_cast<const UnicodeString
*>(u
);
859 // It's a unicode string telling us what type of calendar to load ("gregorian", etc)
860 // Create a Locale over this string
862 LocaleUtility::initLocaleFromName(*str
, l
);
864 #ifdef U_DEBUG_CALSVC
865 fprintf(stderr
, "Calendar::createInstance(%s), looking up [%s]\n", aLocale
.getName(), l
.getName());
872 // Don't overwrite actualLoc, since the actual loc from this call
873 // may be something like "@calendar=gregorian" -- TODO investigate
875 c
= (Calendar
*)getCalendarService(success
)->get(l
, LocaleKey::KIND_ANY
, &actualLoc2
, success
);
877 if(U_FAILURE(success
) || !c
) {
879 if(U_SUCCESS(success
)) {
880 success
= U_INTERNAL_PROGRAM_ERROR
; // Propagate some err
885 str
= dynamic_cast<const UnicodeString
*>(c
);
887 // recursed! Second lookup returned a UnicodeString.
888 // Perhaps DefaultCalendar{} was set to another locale.
889 #ifdef U_DEBUG_CALSVC
891 // Extract a char* out of it..
892 int32_t len
= str
->length();
893 int32_t actLen
= sizeof(tmp
)-1;
897 str
->extract(0,len
,tmp
);
900 fprintf(stderr
, "err - recursed, 2nd lookup was unistring %s\n", tmp
);
902 success
= U_MISSING_RESOURCE_ERROR
; // requested a calendar type which could NOT be found.
907 #ifdef U_DEBUG_CALSVC
908 fprintf(stderr
, "%p: setting week count data to locale %s, actual locale %s\n", c
, (const char*)aLocale
.getName(), (const char *)actualLoc
.getName());
910 c
->setWeekData(aLocale
, c
->getType(), success
); // set the correct locale (this was an indirected calendar)
912 char keyword
[ULOC_FULLNAME_CAPACITY
];
913 UErrorCode tmpStatus
= U_ZERO_ERROR
;
914 l
.getKeywordValue("calendar", keyword
, ULOC_FULLNAME_CAPACITY
, tmpStatus
);
915 if (U_SUCCESS(tmpStatus
) && uprv_strcmp(keyword
, "iso8601") == 0) {
916 c
->setFirstDayOfWeek(UCAL_MONDAY
);
917 c
->setMinimalDaysInFirstWeek(4);
921 #endif /* UCONFIG_NO_SERVICE */
923 // a calendar was returned - we assume the factory did the right thing.
927 // Now, reset calendar to default state:
928 c
->adoptTimeZone(zone
); // Set the correct time zone
929 c
->setTimeInMillis(getNow(), success
); // let the new calendar have the current time.
934 // -------------------------------------
937 Calendar::createInstance(const TimeZone
& zone
, const Locale
& aLocale
, UErrorCode
& success
)
939 Calendar
* c
= createInstance(aLocale
, success
);
940 if(U_SUCCESS(success
) && c
) {
941 c
->setTimeZone(zone
);
946 // -------------------------------------
949 Calendar::operator==(const Calendar
& that
) const
951 UErrorCode status
= U_ZERO_ERROR
;
952 return isEquivalentTo(that
) &&
953 getTimeInMillis(status
) == that
.getTimeInMillis(status
) &&
958 Calendar::isEquivalentTo(const Calendar
& other
) const
960 return typeid(*this) == typeid(other
) &&
961 fLenient
== other
.fLenient
&&
962 fRepeatedWallTime
== other
.fRepeatedWallTime
&&
963 fSkippedWallTime
== other
.fSkippedWallTime
&&
964 fFirstDayOfWeek
== other
.fFirstDayOfWeek
&&
965 fMinimalDaysInFirstWeek
== other
.fMinimalDaysInFirstWeek
&&
966 fWeekendOnset
== other
.fWeekendOnset
&&
967 fWeekendOnsetMillis
== other
.fWeekendOnsetMillis
&&
968 fWeekendCease
== other
.fWeekendCease
&&
969 fWeekendCeaseMillis
== other
.fWeekendCeaseMillis
&&
970 *fZone
== *other
.fZone
;
973 // -------------------------------------
976 Calendar::equals(const Calendar
& when
, UErrorCode
& status
) const
978 return (this == &when
||
979 getTime(status
) == when
.getTime(status
));
982 // -------------------------------------
985 Calendar::before(const Calendar
& when
, UErrorCode
& status
) const
987 return (this != &when
&&
988 getTimeInMillis(status
) < when
.getTimeInMillis(status
));
991 // -------------------------------------
994 Calendar::after(const Calendar
& when
, UErrorCode
& status
) const
996 return (this != &when
&&
997 getTimeInMillis(status
) > when
.getTimeInMillis(status
));
1000 // -------------------------------------
1003 const Locale
* U_EXPORT2
1004 Calendar::getAvailableLocales(int32_t& count
)
1006 return Locale::getAvailableLocales(count
);
1009 // -------------------------------------
1011 StringEnumeration
* U_EXPORT2
1012 Calendar::getKeywordValuesForLocale(const char* key
,
1013 const Locale
& locale
, UBool commonlyUsed
, UErrorCode
& status
)
1015 // This is a wrapper over ucal_getKeywordValuesForLocale
1016 UEnumeration
*uenum
= ucal_getKeywordValuesForLocale(key
, locale
.getName(),
1017 commonlyUsed
, &status
);
1018 if (U_FAILURE(status
)) {
1022 return new UStringEnumeration(uenum
);
1025 // -------------------------------------
1030 return uprv_getUTCtime(); // return as milliseconds
1033 // -------------------------------------
1036 * Gets this Calendar's current time as a long.
1037 * @return the current time as UTC milliseconds from the epoch.
1040 Calendar::getTimeInMillis(UErrorCode
& status
) const
1042 if(U_FAILURE(status
))
1046 ((Calendar
*)this)->updateTime(status
);
1048 /* Test for buffer overflows */
1049 if(U_FAILURE(status
)) {
1055 // -------------------------------------
1058 * Sets this Calendar's current time from the given long value.
1059 * A status of U_ILLEGAL_ARGUMENT_ERROR is set when millis is
1060 * outside the range permitted by a Calendar object when not in lenient mode.
1061 * when in lenient mode the out of range values are pinned to their respective min/max.
1062 * @param date the new time in UTC milliseconds from the epoch.
1065 Calendar::setTimeInMillis( double millis
, UErrorCode
& status
) {
1066 if(U_FAILURE(status
))
1069 if (millis
> MAX_MILLIS
) {
1071 millis
= MAX_MILLIS
;
1073 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1076 } else if (millis
< MIN_MILLIS
) {
1078 millis
= MIN_MILLIS
;
1080 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1086 fAreFieldsSet
= fAreAllFieldsSet
= FALSE
;
1087 fIsTimeSet
= fAreFieldsVirtuallySet
= TRUE
;
1089 for (int32_t i
=0; i
<UCAL_FIELD_COUNT
; ++i
) {
1098 // -------------------------------------
1101 Calendar::get(UCalendarDateFields field
, UErrorCode
& status
) const
1103 // field values are only computed when actually requested; for more on when computation
1104 // of various things happens, see the "data flow in Calendar" description at the top
1106 if (U_SUCCESS(status
)) ((Calendar
*)this)->complete(status
); // Cast away const
1107 return U_SUCCESS(status
) ? fFields
[field
] : 0;
1110 // -------------------------------------
1113 Calendar::set(UCalendarDateFields field
, int32_t value
)
1115 if (fAreFieldsVirtuallySet
) {
1116 UErrorCode ec
= U_ZERO_ERROR
;
1119 fFields
[field
] = value
;
1120 /* Ensure that the fNextStamp value doesn't go pass max value for int32_t */
1121 if (fNextStamp
== STAMP_MAX
) {
1124 fStamp
[field
] = fNextStamp
++;
1125 fIsSet
[field
] = TRUE
; // Remove later
1126 fIsTimeSet
= fAreFieldsSet
= fAreFieldsVirtuallySet
= FALSE
;
1129 // -------------------------------------
1132 Calendar::set(int32_t year
, int32_t month
, int32_t date
)
1134 set(UCAL_YEAR
, year
);
1135 set(UCAL_MONTH
, month
);
1136 set(UCAL_DATE
, date
);
1139 // -------------------------------------
1142 Calendar::set(int32_t year
, int32_t month
, int32_t date
, int32_t hour
, int32_t minute
)
1144 set(UCAL_YEAR
, year
);
1145 set(UCAL_MONTH
, month
);
1146 set(UCAL_DATE
, date
);
1147 set(UCAL_HOUR_OF_DAY
, hour
);
1148 set(UCAL_MINUTE
, minute
);
1151 // -------------------------------------
1154 Calendar::set(int32_t year
, int32_t month
, int32_t date
, int32_t hour
, int32_t minute
, int32_t second
)
1156 set(UCAL_YEAR
, year
);
1157 set(UCAL_MONTH
, month
);
1158 set(UCAL_DATE
, date
);
1159 set(UCAL_HOUR_OF_DAY
, hour
);
1160 set(UCAL_MINUTE
, minute
);
1161 set(UCAL_SECOND
, second
);
1164 // -------------------------------------
1169 for (int32_t i
=0; i
<UCAL_FIELD_COUNT
; ++i
) {
1170 fFields
[i
] = 0; // Must do this; other code depends on it
1172 fIsSet
[i
] = FALSE
; // Remove later
1174 fIsTimeSet
= fAreFieldsSet
= fAreAllFieldsSet
= fAreFieldsVirtuallySet
= FALSE
;
1175 // fTime is not 'cleared' - may be used if no fields are set.
1178 // -------------------------------------
1181 Calendar::clear(UCalendarDateFields field
)
1183 if (fAreFieldsVirtuallySet
) {
1184 UErrorCode ec
= U_ZERO_ERROR
;
1188 fStamp
[field
] = kUnset
;
1189 fIsSet
[field
] = FALSE
; // Remove later
1190 fIsTimeSet
= fAreFieldsSet
= fAreAllFieldsSet
= fAreFieldsVirtuallySet
= FALSE
;
1193 // -------------------------------------
1196 Calendar::isSet(UCalendarDateFields field
) const
1198 return fAreFieldsVirtuallySet
|| (fStamp
[field
] != kUnset
);
1202 int32_t Calendar::newestStamp(UCalendarDateFields first
, UCalendarDateFields last
, int32_t bestStampSoFar
) const
1204 int32_t bestStamp
= bestStampSoFar
;
1205 for (int32_t i
=(int32_t)first
; i
<=(int32_t)last
; ++i
) {
1206 if (fStamp
[i
] > bestStamp
) {
1207 bestStamp
= fStamp
[i
];
1214 // -------------------------------------
1217 Calendar::complete(UErrorCode
& status
)
1221 /* Test for buffer overflows */
1222 if(U_FAILURE(status
)) {
1226 if (!fAreFieldsSet
) {
1227 computeFields(status
); // fills in unset fields
1228 /* Test for buffer overflows */
1229 if(U_FAILURE(status
)) {
1232 fAreFieldsSet
= TRUE
;
1233 fAreAllFieldsSet
= TRUE
;
1237 //-------------------------------------------------------------------------
1238 // Protected utility methods for use by subclasses. These are very handy
1239 // for implementing add, roll, and computeFields.
1240 //-------------------------------------------------------------------------
1243 * Adjust the specified field so that it is within
1244 * the allowable range for the date to which this calendar is set.
1245 * For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH}
1246 * field for a calendar set to April 31 would cause it to be set
1249 * <b>Subclassing:</b>
1251 * This utility method is intended for use by subclasses that need to implement
1252 * their own overrides of {@link #roll roll} and {@link #add add}.
1255 * <code>pinField</code> is implemented in terms of
1256 * {@link #getActualMinimum getActualMinimum}
1257 * and {@link #getActualMaximum getActualMaximum}. If either of those methods uses
1258 * a slow, iterative algorithm for a particular field, it would be
1259 * unwise to attempt to call <code>pinField</code> for that field. If you
1260 * really do need to do so, you should override this method to do
1261 * something more efficient for that field.
1263 * @param field The calendar field whose value should be pinned.
1265 * @see #getActualMinimum
1266 * @see #getActualMaximum
1269 void Calendar::pinField(UCalendarDateFields field
, UErrorCode
& status
) {
1270 int32_t max
= getActualMaximum(field
, status
);
1271 int32_t min
= getActualMinimum(field
, status
);
1273 if (fFields
[field
] > max
) {
1275 } else if (fFields
[field
] < min
) {
1281 void Calendar::computeFields(UErrorCode
&ec
)
1283 if (U_FAILURE(ec
)) {
1286 // Compute local wall millis
1287 double localMillis
= internalGetTime();
1288 int32_t rawOffset
, dstOffset
;
1289 getTimeZone().getOffset(localMillis
, FALSE
, rawOffset
, dstOffset
, ec
);
1290 localMillis
+= (rawOffset
+ dstOffset
);
1292 // Mark fields as set. Do this before calling handleComputeFields().
1293 uint32_t mask
= //fInternalSetMask;
1297 (1 << UCAL_DAY_OF_MONTH
) | // = UCAL_DATE
1298 (1 << UCAL_DAY_OF_YEAR
) |
1299 (1 << UCAL_EXTENDED_YEAR
);
1301 for (int32_t i
=0; i
<UCAL_FIELD_COUNT
; ++i
) {
1302 if ((mask
& 1) == 0) {
1303 fStamp
[i
] = kInternallySet
;
1304 fIsSet
[i
] = TRUE
; // Remove later
1307 fIsSet
[i
] = FALSE
; // Remove later
1312 // We used to check for and correct extreme millis values (near
1313 // Long.MIN_VALUE or Long.MAX_VALUE) here. Such values would cause
1314 // overflows from positive to negative (or vice versa) and had to
1315 // be manually tweaked. We no longer need to do this because we
1316 // have limited the range of supported dates to those that have a
1317 // Julian day that fits into an int. This allows us to implement a
1318 // JULIAN_DAY field and also removes some inelegant code. - Liu
1321 int32_t days
= (int32_t)ClockMath::floorDivide(localMillis
, (double)kOneDay
);
1323 internalSet(UCAL_JULIAN_DAY
,days
+ kEpochStartAsJulianDay
);
1325 #if defined (U_DEBUG_CAL)
1326 //fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n",
1327 //__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
1330 computeGregorianAndDOWFields(fFields
[UCAL_JULIAN_DAY
], ec
);
1332 // Call framework method to have subclass compute its fields.
1333 // These must include, at a minimum, MONTH, DAY_OF_MONTH,
1334 // EXTENDED_YEAR, YEAR, DAY_OF_YEAR. This method will call internalSet(),
1335 // which will update stamp[].
1336 handleComputeFields(fFields
[UCAL_JULIAN_DAY
], ec
);
1338 // Compute week-related fields, based on the subclass-computed
1339 // fields computed by handleComputeFields().
1340 computeWeekFields(ec
);
1342 // Compute time-related fields. These are indepent of the date and
1343 // of the subclass algorithm. They depend only on the local zone
1344 // wall milliseconds in day.
1345 int32_t millisInDay
= (int32_t) (localMillis
- (days
* kOneDay
));
1346 fFields
[UCAL_MILLISECONDS_IN_DAY
] = millisInDay
;
1347 fFields
[UCAL_MILLISECOND
] = millisInDay
% 1000;
1348 millisInDay
/= 1000;
1349 fFields
[UCAL_SECOND
] = millisInDay
% 60;
1351 fFields
[UCAL_MINUTE
] = millisInDay
% 60;
1353 fFields
[UCAL_HOUR_OF_DAY
] = millisInDay
;
1354 fFields
[UCAL_AM_PM
] = millisInDay
/ 12; // Assume AM == 0
1355 fFields
[UCAL_HOUR
] = millisInDay
% 12;
1356 fFields
[UCAL_ZONE_OFFSET
] = rawOffset
;
1357 fFields
[UCAL_DST_OFFSET
] = dstOffset
;
1360 uint8_t Calendar::julianDayToDayOfWeek(double julian
)
1362 // If julian is negative, then julian%7 will be negative, so we adjust
1363 // accordingly. We add 1 because Julian day 0 is Monday.
1364 int8_t dayOfWeek
= (int8_t) uprv_fmod(julian
+ 1, 7);
1366 uint8_t result
= (uint8_t)(dayOfWeek
+ ((dayOfWeek
< 0) ? (7+UCAL_SUNDAY
) : UCAL_SUNDAY
));
1371 * Compute the Gregorian calendar year, month, and day of month from
1372 * the given Julian day. These values are not stored in fields, but in
1373 * member variables gregorianXxx. Also compute the DAY_OF_WEEK and
1376 void Calendar::computeGregorianAndDOWFields(int32_t julianDay
, UErrorCode
&ec
)
1378 computeGregorianFields(julianDay
, ec
);
1380 // Compute day of week: JD 0 = Monday
1381 int32_t dow
= julianDayToDayOfWeek(julianDay
);
1382 internalSet(UCAL_DAY_OF_WEEK
,dow
);
1384 // Calculate 1-based localized day of week
1385 int32_t dowLocal
= dow
- getFirstDayOfWeek() + 1;
1389 internalSet(UCAL_DOW_LOCAL
,dowLocal
);
1390 fFields
[UCAL_DOW_LOCAL
] = dowLocal
;
1394 * Compute the Gregorian calendar year, month, and day of month from the
1395 * Julian day. These values are not stored in fields, but in member
1396 * variables gregorianXxx. They are used for time zone computations and by
1397 * subclasses that are Gregorian derivatives. Subclasses may call this
1398 * method to perform a Gregorian calendar millis->fields computation.
1400 void Calendar::computeGregorianFields(int32_t julianDay
, UErrorCode
& /* ec */) {
1401 int32_t gregorianDayOfWeekUnused
;
1402 Grego::dayToFields(julianDay
- kEpochStartAsJulianDay
, fGregorianYear
, fGregorianMonth
, fGregorianDayOfMonth
, gregorianDayOfWeekUnused
, fGregorianDayOfYear
);
1406 * Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH,
1407 * DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR,
1408 * DAY_OF_WEEK, and DAY_OF_YEAR. The latter fields are computed by the
1409 * subclass based on the calendar system.
1411 * <p>The YEAR_WOY field is computed simplistically. It is equal to YEAR
1412 * most of the time, but at the year boundary it may be adjusted to YEAR-1
1413 * or YEAR+1 to reflect the overlap of a week into an adjacent year. In
1414 * this case, a simple increment or decrement is performed on YEAR, even
1415 * though this may yield an invalid YEAR value. For instance, if the YEAR
1416 * is part of a calendar system with an N-year cycle field CYCLE, then
1417 * incrementing the YEAR may involve incrementing CYCLE and setting YEAR
1418 * back to 0 or 1. This is not handled by this code, and in fact cannot be
1419 * simply handled without having subclasses define an entire parallel set of
1420 * fields for fields larger than or equal to a year. This additional
1421 * complexity is not warranted, since the intention of the YEAR_WOY field is
1422 * to support ISO 8601 notation, so it will typically be used with a
1423 * proleptic Gregorian calendar, which has no field larger than a year.
1425 void Calendar::computeWeekFields(UErrorCode
&ec
) {
1429 int32_t eyear
= fFields
[UCAL_EXTENDED_YEAR
];
1430 int32_t dayOfWeek
= fFields
[UCAL_DAY_OF_WEEK
];
1431 int32_t dayOfYear
= fFields
[UCAL_DAY_OF_YEAR
];
1433 // WEEK_OF_YEAR start
1434 // Compute the week of the year. For the Gregorian calendar, valid week
1435 // numbers run from 1 to 52 or 53, depending on the year, the first day
1436 // of the week, and the minimal days in the first week. For other
1437 // calendars, the valid range may be different -- it depends on the year
1438 // length. Days at the start of the year may fall into the last week of
1439 // the previous year; days at the end of the year may fall into the
1440 // first week of the next year. ASSUME that the year length is less than
1442 int32_t yearOfWeekOfYear
= eyear
;
1443 int32_t relDow
= (dayOfWeek
+ 7 - getFirstDayOfWeek()) % 7; // 0..6
1444 int32_t relDowJan1
= (dayOfWeek
- dayOfYear
+ 7001 - getFirstDayOfWeek()) % 7; // 0..6
1445 int32_t woy
= (dayOfYear
- 1 + relDowJan1
) / 7; // 0..53
1446 if ((7 - relDowJan1
) >= getMinimalDaysInFirstWeek()) {
1450 // Adjust for weeks at the year end that overlap into the previous or
1451 // next calendar year.
1453 // We are the last week of the previous year.
1454 // Check to see if we are in the last week; if so, we need
1455 // to handle the case in which we are the first week of the
1458 int32_t prevDoy
= dayOfYear
+ handleGetYearLength(eyear
- 1);
1459 woy
= weekNumber(prevDoy
, dayOfWeek
);
1462 int32_t lastDoy
= handleGetYearLength(eyear
);
1463 // Fast check: For it to be week 1 of the next year, the DOY
1464 // must be on or after L-5, where L is yearLength(), then it
1465 // cannot possibly be week 1 of the next year:
1467 // doy: 359 360 361 362 363 364 365 001
1468 // dow: 1 2 3 4 5 6 7
1469 if (dayOfYear
>= (lastDoy
- 5)) {
1470 int32_t lastRelDow
= (relDow
+ lastDoy
- dayOfYear
) % 7;
1471 if (lastRelDow
< 0) {
1474 if (((6 - lastRelDow
) >= getMinimalDaysInFirstWeek()) &&
1475 ((dayOfYear
+ 7 - relDow
) > lastDoy
)) {
1481 fFields
[UCAL_WEEK_OF_YEAR
] = woy
;
1482 fFields
[UCAL_YEAR_WOY
] = yearOfWeekOfYear
;
1485 int32_t dayOfMonth
= fFields
[UCAL_DAY_OF_MONTH
];
1486 fFields
[UCAL_WEEK_OF_MONTH
] = weekNumber(dayOfMonth
, dayOfWeek
);
1487 fFields
[UCAL_DAY_OF_WEEK_IN_MONTH
] = (dayOfMonth
-1) / 7 + 1;
1488 #if defined (U_DEBUG_CAL)
1489 if(fFields
[UCAL_DAY_OF_WEEK_IN_MONTH
]==0) fprintf(stderr
, "%s:%d: DOWIM %d on %g\n",
1490 __FILE__
, __LINE__
,fFields
[UCAL_DAY_OF_WEEK_IN_MONTH
], fTime
);
1495 int32_t Calendar::weekNumber(int32_t desiredDay
, int32_t dayOfPeriod
, int32_t dayOfWeek
)
1497 // Determine the day of the week of the first day of the period
1498 // in question (either a year or a month). Zero represents the
1499 // first day of the week on this calendar.
1500 int32_t periodStartDayOfWeek
= (dayOfWeek
- getFirstDayOfWeek() - dayOfPeriod
+ 1) % 7;
1501 if (periodStartDayOfWeek
< 0) periodStartDayOfWeek
+= 7;
1503 // Compute the week number. Initially, ignore the first week, which
1504 // may be fractional (or may not be). We add periodStartDayOfWeek in
1505 // order to fill out the first week, if it is fractional.
1506 int32_t weekNo
= (desiredDay
+ periodStartDayOfWeek
- 1)/7;
1508 // If the first week is long enough, then count it. If
1509 // the minimal days in the first week is one, or if the period start
1510 // is zero, we always increment weekNo.
1511 if ((7 - periodStartDayOfWeek
) >= getMinimalDaysInFirstWeek()) ++weekNo
;
1516 void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode
&/* status */)
1518 internalSet(UCAL_MONTH
, getGregorianMonth());
1519 internalSet(UCAL_DAY_OF_MONTH
, getGregorianDayOfMonth());
1520 internalSet(UCAL_DAY_OF_YEAR
, getGregorianDayOfYear());
1521 int32_t eyear
= getGregorianYear();
1522 internalSet(UCAL_EXTENDED_YEAR
, eyear
);
1523 int32_t era
= GregorianCalendar::AD
;
1525 era
= GregorianCalendar::BC
;
1528 internalSet(UCAL_ERA
, era
);
1529 internalSet(UCAL_YEAR
, eyear
);
1531 // -------------------------------------
1534 void Calendar::roll(EDateFields field
, int32_t amount
, UErrorCode
& status
)
1536 roll((UCalendarDateFields
)field
, amount
, status
);
1539 void Calendar::roll(UCalendarDateFields field
, int32_t amount
, UErrorCode
& status
)
1542 return; // Nothing to do
1547 if(U_FAILURE(status
)) {
1551 case UCAL_DAY_OF_MONTH
:
1555 case UCAL_MILLISECOND
:
1556 case UCAL_MILLISECONDS_IN_DAY
:
1558 // These are the standard roll instructions. These work for all
1559 // simple cases, that is, cases in which the limits are fixed, such
1560 // as the hour, the day of the month, and the era.
1562 int32_t min
= getActualMinimum(field
,status
);
1563 int32_t max
= getActualMaximum(field
,status
);
1564 int32_t gap
= max
- min
+ 1;
1566 int32_t value
= internalGet(field
) + amount
;
1567 value
= (value
- min
) % gap
;
1578 case UCAL_HOUR_OF_DAY
:
1579 // Rolling the hour is difficult on the ONSET and CEASE days of
1580 // daylight savings. For example, if the change occurs at
1581 // 2 AM, we have the following progression:
1582 // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst
1583 // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std
1584 // To get around this problem we don't use fields; we manipulate
1585 // the time in millis directly.
1587 // Assume min == 0 in calculations below
1588 double start
= getTimeInMillis(status
);
1589 int32_t oldHour
= internalGet(field
);
1590 int32_t max
= getMaximum(field
);
1591 int32_t newHour
= (oldHour
+ amount
) % (max
+ 1);
1595 setTimeInMillis(start
+ kOneHour
* (newHour
- oldHour
),status
);
1600 // Rolling the month involves both pinning the final value
1601 // and adjusting the DAY_OF_MONTH if necessary. We only adjust the
1602 // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
1603 // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
1605 int32_t max
= getActualMaximum(UCAL_MONTH
, status
);
1606 int32_t mon
= (internalGet(UCAL_MONTH
) + amount
) % (max
+1);
1611 set(UCAL_MONTH
, mon
);
1613 // Keep the day of month in range. We don't want to spill over
1614 // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 ->
1616 pinField(UCAL_DAY_OF_MONTH
,status
);
1623 // * If era==0 and years go backwards in time, change sign of amount.
1624 // * Until we have new API per #9393, we temporarily hardcode knowledge of
1625 // which calendars have era 0 years that go backwards.
1626 UBool era0WithYearsThatGoBackwards
= FALSE
;
1627 int32_t era
= get(UCAL_ERA
, status
);
1629 const char * calType
= getType();
1630 if ( uprv_strcmp(calType
,"gregorian")==0 || uprv_strcmp(calType
,"roc")==0 || uprv_strcmp(calType
,"coptic")==0 ) {
1632 era0WithYearsThatGoBackwards
= TRUE
;
1635 int32_t newYear
= internalGet(field
) + amount
;
1636 if (era
> 0 || newYear
>= 1) {
1637 int32_t maxYear
= getActualMaximum(field
, status
);
1638 if (maxYear
< 32768) {
1639 // this era has real bounds, roll should wrap years
1641 newYear
= maxYear
- ((-newYear
) % maxYear
);
1642 } else if (newYear
> maxYear
) {
1643 newYear
= ((newYear
- 1) % maxYear
) + 1;
1645 // else era is unbounded, just pin low year instead of wrapping
1646 } else if (newYear
< 1) {
1649 // else we are in era 0 with newYear < 1;
1650 // calendars with years that go backwards must pin the year value at 0,
1651 // other calendars can have years < 0 in era 0
1652 } else if (era0WithYearsThatGoBackwards
) {
1655 set(field
, newYear
);
1656 pinField(UCAL_MONTH
,status
);
1657 pinField(UCAL_DAY_OF_MONTH
,status
);
1661 case UCAL_EXTENDED_YEAR
:
1662 // Rolling the year can involve pinning the DAY_OF_MONTH.
1663 set(field
, internalGet(field
) + amount
);
1664 pinField(UCAL_MONTH
,status
);
1665 pinField(UCAL_DAY_OF_MONTH
,status
);
1668 case UCAL_WEEK_OF_MONTH
:
1670 // This is tricky, because during the roll we may have to shift
1671 // to a different day of the week. For example:
1677 // When rolling from the 6th or 7th back one week, we go to the
1678 // 1st (assuming that the first partial week counts). The same
1679 // thing happens at the end of the month.
1681 // The other tricky thing is that we have to figure out whether
1682 // the first partial week actually counts or not, based on the
1683 // minimal first days in the week. And we have to use the
1684 // correct first day of the week to delineate the week
1687 // Here's our algorithm. First, we find the real boundaries of
1688 // the month. Then we discard the first partial week if it
1689 // doesn't count in this locale. Then we fill in the ends with
1690 // phantom days, so that the first partial week and the last
1691 // partial week are full weeks. We then have a nice square
1692 // block of weeks. We do the usual rolling within this block,
1693 // as is done elsewhere in this method. If we wind up on one of
1694 // the phantom days that we added, we recognize this and pin to
1695 // the first or the last day of the month. Easy, eh?
1697 // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
1698 // in this locale. We have dow in 0..6.
1699 int32_t dow
= internalGet(UCAL_DAY_OF_WEEK
) - getFirstDayOfWeek();
1700 if (dow
< 0) dow
+= 7;
1702 // Find the day of the week (normalized for locale) for the first
1704 int32_t fdm
= (dow
- internalGet(UCAL_DAY_OF_MONTH
) + 1) % 7;
1705 if (fdm
< 0) fdm
+= 7;
1707 // Get the first day of the first full week of the month,
1708 // including phantom days, if any. Figure out if the first week
1709 // counts or not; if it counts, then fill in phantom days. If
1710 // not, advance to the first real full week (skip the partial week).
1712 if ((7 - fdm
) < getMinimalDaysInFirstWeek())
1713 start
= 8 - fdm
; // Skip the first partial week
1715 start
= 1 - fdm
; // This may be zero or negative
1717 // Get the day of the week (normalized for locale) for the last
1718 // day of the month.
1719 int32_t monthLen
= getActualMaximum(UCAL_DAY_OF_MONTH
, status
);
1720 int32_t ldm
= (monthLen
- internalGet(UCAL_DAY_OF_MONTH
) + dow
) % 7;
1721 // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.
1723 // Get the limit day for the blocked-off rectangular month; that
1724 // is, the day which is one past the last day of the month,
1725 // after the month has already been filled in with phantom days
1726 // to fill out the last week. This day has a normalized DOW of 0.
1727 int32_t limit
= monthLen
+ 7 - ldm
;
1729 // Now roll between start and (limit - 1).
1730 int32_t gap
= limit
- start
;
1731 int32_t day_of_month
= (internalGet(UCAL_DAY_OF_MONTH
) + amount
*7 -
1733 if (day_of_month
< 0) day_of_month
+= gap
;
1734 day_of_month
+= start
;
1736 // Finally, pin to the real start and end of the month.
1737 if (day_of_month
< 1) day_of_month
= 1;
1738 if (day_of_month
> monthLen
) day_of_month
= monthLen
;
1740 // Set the DAY_OF_MONTH. We rely on the fact that this field
1741 // takes precedence over everything else (since all other fields
1742 // are also set at this point). If this fact changes (if the
1743 // disambiguation algorithm changes) then we will have to unset
1744 // the appropriate fields here so that DAY_OF_MONTH is attended
1746 set(UCAL_DAY_OF_MONTH
, day_of_month
);
1749 case UCAL_WEEK_OF_YEAR
:
1751 // This follows the outline of WEEK_OF_MONTH, except it applies
1752 // to the whole year. Please see the comment for WEEK_OF_MONTH
1753 // for general notes.
1755 // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
1756 // in this locale. We have dow in 0..6.
1757 int32_t dow
= internalGet(UCAL_DAY_OF_WEEK
) - getFirstDayOfWeek();
1758 if (dow
< 0) dow
+= 7;
1760 // Find the day of the week (normalized for locale) for the first
1762 int32_t fdy
= (dow
- internalGet(UCAL_DAY_OF_YEAR
) + 1) % 7;
1763 if (fdy
< 0) fdy
+= 7;
1765 // Get the first day of the first full week of the year,
1766 // including phantom days, if any. Figure out if the first week
1767 // counts or not; if it counts, then fill in phantom days. If
1768 // not, advance to the first real full week (skip the partial week).
1770 if ((7 - fdy
) < getMinimalDaysInFirstWeek())
1771 start
= 8 - fdy
; // Skip the first partial week
1773 start
= 1 - fdy
; // This may be zero or negative
1775 // Get the day of the week (normalized for locale) for the last
1777 int32_t yearLen
= getActualMaximum(UCAL_DAY_OF_YEAR
,status
);
1778 int32_t ldy
= (yearLen
- internalGet(UCAL_DAY_OF_YEAR
) + dow
) % 7;
1779 // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here.
1781 // Get the limit day for the blocked-off rectangular year; that
1782 // is, the day which is one past the last day of the year,
1783 // after the year has already been filled in with phantom days
1784 // to fill out the last week. This day has a normalized DOW of 0.
1785 int32_t limit
= yearLen
+ 7 - ldy
;
1787 // Now roll between start and (limit - 1).
1788 int32_t gap
= limit
- start
;
1789 int32_t day_of_year
= (internalGet(UCAL_DAY_OF_YEAR
) + amount
*7 -
1791 if (day_of_year
< 0) day_of_year
+= gap
;
1792 day_of_year
+= start
;
1794 // Finally, pin to the real start and end of the month.
1795 if (day_of_year
< 1) day_of_year
= 1;
1796 if (day_of_year
> yearLen
) day_of_year
= yearLen
;
1798 // Make sure that the year and day of year are attended to by
1799 // clearing other fields which would normally take precedence.
1800 // If the disambiguation algorithm is changed, this section will
1801 // have to be updated as well.
1802 set(UCAL_DAY_OF_YEAR
, day_of_year
);
1806 case UCAL_DAY_OF_YEAR
:
1808 // Roll the day of year using millis. Compute the millis for
1809 // the start of the year, and get the length of the year.
1810 double delta
= amount
* kOneDay
; // Scale up from days to millis
1811 double min2
= internalGet(UCAL_DAY_OF_YEAR
)-1;
1813 min2
= internalGetTime() - min2
;
1815 // double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay;
1818 double yearLength
= getActualMaximum(UCAL_DAY_OF_YEAR
,status
);
1819 double oneYear
= yearLength
;
1821 newtime
= uprv_fmod((internalGetTime() + delta
- min2
), oneYear
);
1822 if (newtime
< 0) newtime
+= oneYear
;
1823 setTimeInMillis(newtime
+ min2
, status
);
1826 case UCAL_DAY_OF_WEEK
:
1827 case UCAL_DOW_LOCAL
:
1829 // Roll the day of week using millis. Compute the millis for
1830 // the start of the week, using the first day of week setting.
1831 // Restrict the millis to [start, start+7days).
1832 double delta
= amount
* kOneDay
; // Scale up from days to millis
1833 // Compute the number of days before the current day in this
1834 // week. This will be a value 0..6.
1835 int32_t leadDays
= internalGet(field
);
1836 leadDays
-= (field
== UCAL_DAY_OF_WEEK
) ? getFirstDayOfWeek() : 1;
1837 if (leadDays
< 0) leadDays
+= 7;
1838 double min2
= internalGetTime() - leadDays
* kOneDay
;
1839 double newtime
= uprv_fmod((internalGetTime() + delta
- min2
), kOneWeek
);
1840 if (newtime
< 0) newtime
+= kOneWeek
;
1841 setTimeInMillis(newtime
+ min2
, status
);
1844 case UCAL_DAY_OF_WEEK_IN_MONTH
:
1846 // Roll the day of week in the month using millis. Determine
1847 // the first day of the week in the month, and then the last,
1848 // and then roll within that range.
1849 double delta
= amount
* kOneWeek
; // Scale up from weeks to millis
1850 // Find the number of same days of the week before this one
1852 int32_t preWeeks
= (internalGet(UCAL_DAY_OF_MONTH
) - 1) / 7;
1853 // Find the number of same days of the week after this one
1855 int32_t postWeeks
= (getActualMaximum(UCAL_DAY_OF_MONTH
,status
) -
1856 internalGet(UCAL_DAY_OF_MONTH
)) / 7;
1857 // From these compute the min and gap millis for rolling.
1858 double min2
= internalGetTime() - preWeeks
* kOneWeek
;
1859 double gap2
= kOneWeek
* (preWeeks
+ postWeeks
+ 1); // Must add 1!
1860 // Roll within this range
1861 double newtime
= uprv_fmod((internalGetTime() + delta
- min2
), gap2
);
1862 if (newtime
< 0) newtime
+= gap2
;
1863 setTimeInMillis(newtime
+ min2
, status
);
1866 case UCAL_JULIAN_DAY
:
1867 set(field
, internalGet(field
) + amount
);
1870 // Other fields cannot be rolled by this method
1871 #if defined (U_DEBUG_CAL)
1872 fprintf(stderr
, "%s:%d: ILLEGAL ARG because of roll on non-rollable field %s\n",
1873 __FILE__
, __LINE__
,fldName(field
));
1875 status
= U_ILLEGAL_ARGUMENT_ERROR
;
1879 void Calendar::add(EDateFields field
, int32_t amount
, UErrorCode
& status
)
1881 Calendar::add((UCalendarDateFields
)field
, amount
, status
);
1884 // -------------------------------------
1885 void Calendar::add(UCalendarDateFields field
, int32_t amount
, UErrorCode
& status
)
1888 return; // Do nothing!
1891 // We handle most fields in the same way. The algorithm is to add
1892 // a computed amount of millis to the current millis. The only
1893 // wrinkle is with DST (and/or a change to the zone's UTC offset, which
1894 // we'll include with DST) -- for some fields, like the DAY_OF_MONTH,
1895 // we don't want the HOUR to shift due to changes in DST. If the
1896 // result of the add operation is to move from DST to Standard, or
1897 // vice versa, we need to adjust by an hour forward or back,
1898 // respectively. For such fields we set keepHourInvariant to TRUE.
1900 // We only adjust the DST for fields larger than an hour. For
1901 // fields smaller than an hour, we cannot adjust for DST without
1902 // causing problems. for instance, if you add one hour to April 5,
1903 // 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an
1904 // illegal value), but then the adjustment sees the change and
1905 // compensates by subtracting an hour. As a result the time
1906 // doesn't advance at all.
1908 // For some fields larger than a day, such as a UCAL_MONTH, we pin the
1909 // UCAL_DAY_OF_MONTH. This allows <March 31>.add(UCAL_MONTH, 1) to be
1910 // <April 30>, rather than <April 31> => <May 1>.
1912 double delta
= amount
; // delta in ms
1913 UBool keepHourInvariant
= TRUE
;
1917 set(field
, get(field
, status
) + amount
);
1918 pinField(UCAL_ERA
, status
);
1924 // * If era=0 and years go backwards in time, change sign of amount.
1925 // * Until we have new API per #9393, we temporarily hardcode knowledge of
1926 // which calendars have era 0 years that go backwards.
1927 // * Note that for UCAL_YEAR (but not UCAL_YEAR_WOY) we could instead handle
1928 // this by applying the amount to the UCAL_EXTENDED_YEAR field; but since
1929 // we would still need to handle UCAL_YEAR_WOY as below, might as well
1930 // also handle UCAL_YEAR the same way.
1931 int32_t era
= get(UCAL_ERA
, status
);
1933 const char * calType
= getType();
1934 if ( uprv_strcmp(calType
,"gregorian")==0 || uprv_strcmp(calType
,"roc")==0 || uprv_strcmp(calType
,"coptic")==0 ) {
1939 // Fall through into normal handling
1940 case UCAL_EXTENDED_YEAR
:
1943 UBool oldLenient
= isLenient();
1945 set(field
, get(field
, status
) + amount
);
1946 pinField(UCAL_DAY_OF_MONTH
, status
);
1947 if(oldLenient
==FALSE
) {
1948 complete(status
); /* force recalculate */
1949 setLenient(oldLenient
);
1954 case UCAL_WEEK_OF_YEAR
:
1955 case UCAL_WEEK_OF_MONTH
:
1956 case UCAL_DAY_OF_WEEK_IN_MONTH
:
1961 delta
*= 12 * kOneHour
;
1964 case UCAL_DAY_OF_MONTH
:
1965 case UCAL_DAY_OF_YEAR
:
1966 case UCAL_DAY_OF_WEEK
:
1967 case UCAL_DOW_LOCAL
:
1968 case UCAL_JULIAN_DAY
:
1972 case UCAL_HOUR_OF_DAY
:
1975 keepHourInvariant
= FALSE
;
1979 delta
*= kOneMinute
;
1980 keepHourInvariant
= FALSE
;
1984 delta
*= kOneSecond
;
1985 keepHourInvariant
= FALSE
;
1988 case UCAL_MILLISECOND
:
1989 case UCAL_MILLISECONDS_IN_DAY
:
1990 keepHourInvariant
= FALSE
;
1994 #if defined (U_DEBUG_CAL)
1995 fprintf(stderr
, "%s:%d: ILLEGAL ARG because field %s not addable",
1996 __FILE__
, __LINE__
, fldName(field
));
1998 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2000 // throw new IllegalArgumentException("Calendar.add(" + fieldName(field) +
2001 // ") not supported");
2004 // In order to keep the hour invariant (for fields where this is
2005 // appropriate), check the combined DST & ZONE offset before and
2006 // after the add() operation. If it changes, then adjust the millis
2008 int32_t prevOffset
= 0;
2010 if (keepHourInvariant
) {
2011 prevOffset
= get(UCAL_DST_OFFSET
, status
) + get(UCAL_ZONE_OFFSET
, status
);
2012 hour
= internalGet(UCAL_HOUR_OF_DAY
);
2015 setTimeInMillis(getTimeInMillis(status
) + delta
, status
);
2017 if (keepHourInvariant
) {
2018 int32_t newOffset
= get(UCAL_DST_OFFSET
, status
) + get(UCAL_ZONE_OFFSET
, status
);
2019 if (newOffset
!= prevOffset
) {
2020 // We have done an hour-invariant adjustment but the
2021 // combined offset has changed. We adjust millis to keep
2022 // the hour constant. In cases such as midnight after
2023 // a DST change which occurs at midnight, there is the
2024 // danger of adjusting into a different day. To avoid
2025 // this we make the adjustment only if it actually
2026 // maintains the hour.
2028 // When the difference of the previous UTC offset and
2029 // the new UTC offset exceeds 1 full day, we do not want
2030 // to roll over/back the date. For now, this only happens
2031 // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452.
2032 int32_t adjAmount
= prevOffset
- newOffset
;
2033 adjAmount
= adjAmount
>= 0 ? adjAmount
% (int32_t)kOneDay
: -(-adjAmount
% (int32_t)kOneDay
);
2034 if (adjAmount
!= 0) {
2035 double t
= internalGetTime();
2036 setTimeInMillis(t
+ adjAmount
, status
);
2037 if (get(UCAL_HOUR_OF_DAY
, status
) != hour
) {
2038 setTimeInMillis(t
, status
);
2045 // -------------------------------------
2046 int32_t Calendar::fieldDifference(UDate when
, EDateFields field
, UErrorCode
& status
) {
2047 return fieldDifference(when
, (UCalendarDateFields
) field
, status
);
2050 int32_t Calendar::fieldDifference(UDate targetMs
, UCalendarDateFields field
, UErrorCode
& ec
) {
2051 if (U_FAILURE(ec
)) return 0;
2053 double startMs
= getTimeInMillis(ec
);
2054 // Always add from the start millis. This accomodates
2055 // operations like adding years from February 29, 2000 up to
2056 // February 29, 2004. If 1, 1, 1, 1 is added to the year
2057 // field, the DOM gets pinned to 28 and stays there, giving an
2058 // incorrect DOM difference of 1. We have to add 1, reset, 2,
2059 // reset, 3, reset, 4.
2060 if (startMs
< targetMs
) {
2062 // Find a value that is too large
2063 while (U_SUCCESS(ec
)) {
2064 setTimeInMillis(startMs
, ec
);
2065 add(field
, max
, ec
);
2066 double ms
= getTimeInMillis(ec
);
2067 if (ms
== targetMs
) {
2069 } else if (ms
> targetMs
) {
2071 } else if (max
< INT32_MAX
) {
2078 // Field difference too large to fit into int32_t
2079 #if defined (U_DEBUG_CAL)
2080 fprintf(stderr
, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
2081 __FILE__
, __LINE__
, fldName(field
));
2083 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
2086 // Do a binary search
2087 while ((max
- min
) > 1 && U_SUCCESS(ec
)) {
2088 int32_t t
= min
+ (max
- min
)/2; // make sure intermediate values don't exceed INT32_MAX
2089 setTimeInMillis(startMs
, ec
);
2091 double ms
= getTimeInMillis(ec
);
2092 if (ms
== targetMs
) {
2094 } else if (ms
> targetMs
) {
2100 } else if (startMs
> targetMs
) {
2102 // Find a value that is too small
2103 while (U_SUCCESS(ec
)) {
2104 setTimeInMillis(startMs
, ec
);
2105 add(field
, max
, ec
);
2106 double ms
= getTimeInMillis(ec
);
2107 if (ms
== targetMs
) {
2109 } else if (ms
< targetMs
) {
2115 // Field difference too large to fit into int32_t
2116 #if defined (U_DEBUG_CAL)
2117 fprintf(stderr
, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
2118 __FILE__
, __LINE__
, fldName(field
));
2120 ec
= U_ILLEGAL_ARGUMENT_ERROR
;
2124 // Do a binary search
2125 while ((min
- max
) > 1 && U_SUCCESS(ec
)) {
2126 int32_t t
= min
+ (max
- min
)/2; // make sure intermediate values don't exceed INT32_MAX
2127 setTimeInMillis(startMs
, ec
);
2129 double ms
= getTimeInMillis(ec
);
2130 if (ms
== targetMs
) {
2132 } else if (ms
< targetMs
) {
2139 // Set calendar to end point
2140 setTimeInMillis(startMs
, ec
);
2141 add(field
, min
, ec
);
2143 /* Test for buffer overflows */
2150 // -------------------------------------
2153 Calendar::adoptTimeZone(TimeZone
* zone
)
2155 // Do nothing if passed-in zone is NULL
2156 if (zone
== NULL
) return;
2158 // fZone should always be non-null
2159 if (fZone
!= NULL
) delete fZone
;
2162 // if the zone changes, we need to recompute the time fields
2163 fAreFieldsSet
= FALSE
;
2166 // -------------------------------------
2168 Calendar::setTimeZone(const TimeZone
& zone
)
2170 adoptTimeZone(zone
.clone());
2173 // -------------------------------------
2176 Calendar::getTimeZone() const
2181 // -------------------------------------
2184 Calendar::orphanTimeZone()
2186 TimeZone
*z
= fZone
;
2187 // we let go of the time zone; the new time zone is the system default time zone
2188 fZone
= TimeZone::createDefault();
2192 // -------------------------------------
2195 Calendar::setLenient(UBool lenient
)
2200 // -------------------------------------
2203 Calendar::isLenient() const
2208 // -------------------------------------
2211 Calendar::setRepeatedWallTimeOption(UCalendarWallTimeOption option
)
2213 if (option
== UCAL_WALLTIME_LAST
|| option
== UCAL_WALLTIME_FIRST
) {
2214 fRepeatedWallTime
= option
;
2218 // -------------------------------------
2220 UCalendarWallTimeOption
2221 Calendar::getRepeatedWallTimeOption(void) const
2223 return fRepeatedWallTime
;
2226 // -------------------------------------
2229 Calendar::setSkippedWallTimeOption(UCalendarWallTimeOption option
)
2231 fSkippedWallTime
= option
;
2234 // -------------------------------------
2236 UCalendarWallTimeOption
2237 Calendar::getSkippedWallTimeOption(void) const
2239 return fSkippedWallTime
;
2242 // -------------------------------------
2245 Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value
)
2247 if (fFirstDayOfWeek
!= value
&&
2248 value
>= UCAL_SUNDAY
&& value
<= UCAL_SATURDAY
) {
2249 fFirstDayOfWeek
= value
;
2250 fAreFieldsSet
= FALSE
;
2254 // -------------------------------------
2256 Calendar::EDaysOfWeek
2257 Calendar::getFirstDayOfWeek() const
2259 return (Calendar::EDaysOfWeek
)fFirstDayOfWeek
;
2263 Calendar::getFirstDayOfWeek(UErrorCode
& /*status*/) const
2265 return fFirstDayOfWeek
;
2267 // -------------------------------------
2270 Calendar::setMinimalDaysInFirstWeek(uint8_t value
)
2272 // Values less than 1 have the same effect as 1; values greater
2273 // than 7 have the same effect as 7. However, we normalize values
2274 // so operator== and so forth work.
2277 } else if (value
> 7) {
2280 if (fMinimalDaysInFirstWeek
!= value
) {
2281 fMinimalDaysInFirstWeek
= value
;
2282 fAreFieldsSet
= FALSE
;
2286 // -------------------------------------
2289 Calendar::getMinimalDaysInFirstWeek() const
2291 return fMinimalDaysInFirstWeek
;
2294 // -------------------------------------
2295 // weekend functions, just dummy implementations for now (for API freeze)
2297 UCalendarWeekdayType
2298 Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek
, UErrorCode
&status
) const
2300 if (U_FAILURE(status
)) {
2301 return UCAL_WEEKDAY
;
2303 if (dayOfWeek
< UCAL_SUNDAY
|| dayOfWeek
> UCAL_SATURDAY
) {
2304 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2305 return UCAL_WEEKDAY
;
2307 if (fWeekendOnset
== fWeekendCease
) {
2308 if (dayOfWeek
!= fWeekendOnset
)
2309 return UCAL_WEEKDAY
;
2310 return (fWeekendOnsetMillis
== 0) ? UCAL_WEEKEND
: UCAL_WEEKEND_ONSET
;
2312 if (fWeekendOnset
< fWeekendCease
) {
2313 if (dayOfWeek
< fWeekendOnset
|| dayOfWeek
> fWeekendCease
) {
2314 return UCAL_WEEKDAY
;
2317 if (dayOfWeek
> fWeekendCease
&& dayOfWeek
< fWeekendOnset
) {
2318 return UCAL_WEEKDAY
;
2321 if (dayOfWeek
== fWeekendOnset
) {
2322 return (fWeekendOnsetMillis
== 0) ? UCAL_WEEKEND
: UCAL_WEEKEND_ONSET
;
2324 if (dayOfWeek
== fWeekendCease
) {
2325 return (fWeekendCeaseMillis
>= 86400000) ? UCAL_WEEKEND
: UCAL_WEEKEND_CEASE
;
2327 return UCAL_WEEKEND
;
2331 Calendar::getWeekendTransition(UCalendarDaysOfWeek dayOfWeek
, UErrorCode
&status
) const
2333 if (U_FAILURE(status
)) {
2336 if (dayOfWeek
== fWeekendOnset
) {
2337 return fWeekendOnsetMillis
;
2338 } else if (dayOfWeek
== fWeekendCease
) {
2339 return fWeekendCeaseMillis
;
2341 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2346 Calendar::isWeekend(UDate date
, UErrorCode
&status
) const
2348 if (U_FAILURE(status
)) {
2351 // clone the calendar so we don't mess with the real one.
2352 Calendar
*work
= (Calendar
*)this->clone();
2354 status
= U_MEMORY_ALLOCATION_ERROR
;
2357 UBool result
= FALSE
;
2358 work
->setTime(date
, status
);
2359 if (U_SUCCESS(status
)) {
2360 result
= work
->isWeekend();
2367 Calendar::isWeekend(void) const
2369 UErrorCode status
= U_ZERO_ERROR
;
2370 UCalendarDaysOfWeek dayOfWeek
= (UCalendarDaysOfWeek
)get(UCAL_DAY_OF_WEEK
, status
);
2371 UCalendarWeekdayType dayType
= getDayOfWeekType(dayOfWeek
, status
);
2372 if (U_SUCCESS(status
)) {
2378 case UCAL_WEEKEND_ONSET
:
2379 case UCAL_WEEKEND_CEASE
:
2380 // Use internalGet() because the above call to get() populated all fields.
2382 int32_t millisInDay
= internalGet(UCAL_MILLISECONDS_IN_DAY
);
2383 int32_t transitionMillis
= getWeekendTransition(dayOfWeek
, status
);
2384 if (U_SUCCESS(status
)) {
2385 return (dayType
== UCAL_WEEKEND_ONSET
)?
2386 (millisInDay
>= transitionMillis
):
2387 (millisInDay
< transitionMillis
);
2389 // else fall through, return FALSE
2398 // ------------------------------------- limits
2401 Calendar::getMinimum(EDateFields field
) const {
2402 return getLimit((UCalendarDateFields
) field
,UCAL_LIMIT_MINIMUM
);
2406 Calendar::getMinimum(UCalendarDateFields field
) const
2408 return getLimit(field
,UCAL_LIMIT_MINIMUM
);
2411 // -------------------------------------
2413 Calendar::getMaximum(EDateFields field
) const
2415 return getLimit((UCalendarDateFields
) field
,UCAL_LIMIT_MAXIMUM
);
2419 Calendar::getMaximum(UCalendarDateFields field
) const
2421 return getLimit(field
,UCAL_LIMIT_MAXIMUM
);
2424 // -------------------------------------
2426 Calendar::getGreatestMinimum(EDateFields field
) const
2428 return getLimit((UCalendarDateFields
)field
,UCAL_LIMIT_GREATEST_MINIMUM
);
2432 Calendar::getGreatestMinimum(UCalendarDateFields field
) const
2434 return getLimit(field
,UCAL_LIMIT_GREATEST_MINIMUM
);
2437 // -------------------------------------
2439 Calendar::getLeastMaximum(EDateFields field
) const
2441 return getLimit((UCalendarDateFields
) field
,UCAL_LIMIT_LEAST_MAXIMUM
);
2445 Calendar::getLeastMaximum(UCalendarDateFields field
) const
2447 return getLimit( field
,UCAL_LIMIT_LEAST_MAXIMUM
);
2450 // -------------------------------------
2452 Calendar::getActualMinimum(EDateFields field
, UErrorCode
& status
) const
2454 return getActualMinimum((UCalendarDateFields
) field
, status
);
2457 int32_t Calendar::getLimit(UCalendarDateFields field
, ELimitType limitType
) const {
2459 case UCAL_DAY_OF_WEEK
:
2462 case UCAL_HOUR_OF_DAY
:
2465 case UCAL_MILLISECOND
:
2466 case UCAL_ZONE_OFFSET
:
2467 case UCAL_DST_OFFSET
:
2468 case UCAL_DOW_LOCAL
:
2469 case UCAL_JULIAN_DAY
:
2470 case UCAL_MILLISECONDS_IN_DAY
:
2471 case UCAL_IS_LEAP_MONTH
:
2472 return kCalendarLimits
[field
][limitType
];
2474 case UCAL_WEEK_OF_MONTH
:
2477 if (limitType
== UCAL_LIMIT_MINIMUM
) {
2478 limit
= getMinimalDaysInFirstWeek() == 1 ? 1 : 0;
2479 } else if (limitType
== UCAL_LIMIT_GREATEST_MINIMUM
) {
2482 int32_t minDaysInFirst
= getMinimalDaysInFirstWeek();
2483 int32_t daysInMonth
= handleGetLimit(UCAL_DAY_OF_MONTH
, limitType
);
2484 if (limitType
== UCAL_LIMIT_LEAST_MAXIMUM
) {
2485 limit
= (daysInMonth
+ (7 - minDaysInFirst
)) / 7;
2486 } else { // limitType == UCAL_LIMIT_MAXIMUM
2487 limit
= (daysInMonth
+ 6 + (7 - minDaysInFirst
)) / 7;
2493 return handleGetLimit(field
, limitType
);
2499 Calendar::getActualMinimum(UCalendarDateFields field
, UErrorCode
& status
) const
2501 int32_t fieldValue
= getGreatestMinimum(field
);
2502 int32_t endValue
= getMinimum(field
);
2504 // if we know that the minimum value is always the same, just return it
2505 if (fieldValue
== endValue
) {
2509 // clone the calendar so we don't mess with the real one, and set it to
2510 // accept anything for the field values
2511 Calendar
*work
= (Calendar
*)this->clone();
2513 status
= U_MEMORY_ALLOCATION_ERROR
;
2516 work
->setLenient(TRUE
);
2518 // now try each value from getLeastMaximum() to getMaximum() one by one until
2519 // we get a value that normalizes to another value. The last value that
2520 // normalizes to itself is the actual minimum for the current date
2521 int32_t result
= fieldValue
;
2524 work
->set(field
, fieldValue
);
2525 if (work
->get(field
, status
) != fieldValue
) {
2529 result
= fieldValue
;
2532 } while (fieldValue
>= endValue
);
2536 /* Test for buffer overflows */
2537 if(U_FAILURE(status
)) {
2543 // -------------------------------------
2548 * Ensure that each field is within its valid range by calling {@link
2549 * #validateField(int)} on each field that has been set. This method
2550 * should only be called if this calendar is not lenient.
2552 * @see #validateField(int)
2554 void Calendar::validateFields(UErrorCode
&status
) {
2555 for (int32_t field
= 0; U_SUCCESS(status
) && (field
< UCAL_FIELD_COUNT
); field
++) {
2556 if (fStamp
[field
] >= kMinimumUserStamp
) {
2557 validateField((UCalendarDateFields
)field
, status
);
2563 * Validate a single field of this calendar. Subclasses should
2564 * override this method to validate any calendar-specific fields.
2565 * Generic fields can be handled by
2566 * <code>Calendar.validateField()</code>.
2567 * @see #validateField(int, int, int)
2569 void Calendar::validateField(UCalendarDateFields field
, UErrorCode
&status
) {
2572 case UCAL_DAY_OF_MONTH
:
2573 y
= handleGetExtendedYear();
2574 validateField(field
, 1, handleGetMonthLength(y
, internalGet(UCAL_MONTH
)), status
);
2576 case UCAL_DAY_OF_YEAR
:
2577 y
= handleGetExtendedYear();
2578 validateField(field
, 1, handleGetYearLength(y
), status
);
2580 case UCAL_DAY_OF_WEEK_IN_MONTH
:
2581 if (internalGet(field
) == 0) {
2582 #if defined (U_DEBUG_CAL)
2583 fprintf(stderr
, "%s:%d: ILLEGAL ARG because DOW in month cannot be 0\n",
2584 __FILE__
, __LINE__
);
2586 status
= U_ILLEGAL_ARGUMENT_ERROR
; // "DAY_OF_WEEK_IN_MONTH cannot be zero"
2589 validateField(field
, getMinimum(field
), getMaximum(field
), status
);
2592 validateField(field
, getMinimum(field
), getMaximum(field
), status
);
2598 * Validate a single field of this calendar given its minimum and
2599 * maximum allowed value. If the field is out of range, throw a
2600 * descriptive <code>IllegalArgumentException</code>. Subclasses may
2601 * use this method in their implementation of {@link
2602 * #validateField(int)}.
2604 void Calendar::validateField(UCalendarDateFields field
, int32_t min
, int32_t max
, UErrorCode
& status
)
2606 int32_t value
= fFields
[field
];
2607 if (value
< min
|| value
> max
) {
2608 #if defined (U_DEBUG_CAL)
2609 fprintf(stderr
, "%s:%d: ILLEGAL ARG because of field %s out of range %d..%d at %d\n",
2610 __FILE__
, __LINE__
,fldName(field
),min
,max
,value
);
2612 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2617 // -------------------------
2619 const UFieldResolutionTable
* Calendar::getFieldResolutionTable() const {
2620 return kDatePrecedence
;
2624 UCalendarDateFields
Calendar::newerField(UCalendarDateFields defaultField
, UCalendarDateFields alternateField
) const
2626 if (fStamp
[alternateField
] > fStamp
[defaultField
]) {
2627 return alternateField
;
2629 return defaultField
;
2632 UCalendarDateFields
Calendar::resolveFields(const UFieldResolutionTable
* precedenceTable
) {
2633 int32_t bestField
= UCAL_FIELD_COUNT
;
2634 int32_t tempBestField
;
2635 UBool restoreWeekOfInternalSet
= FALSE
;
2636 if (fStamp
[UCAL_DAY_OF_WEEK
] >= kMinimumUserStamp
&&
2637 fStamp
[UCAL_DATE
] >= kMinimumUserStamp
&&
2638 fStamp
[UCAL_MONTH
] >= kMinimumUserStamp
&&
2639 fStamp
[UCAL_WEEK_OF_YEAR
] == kInternallySet
&&
2640 fStamp
[UCAL_WEEK_OF_MONTH
] == kInternallySet
&&
2641 fStamp
[UCAL_DAY_OF_WEEK_IN_MONTH
] == kInternallySet
) {
2642 int32_t monthStampDelta
= fStamp
[UCAL_DAY_OF_WEEK
] - fStamp
[UCAL_MONTH
];
2643 int32_t dateStampDelta
= fStamp
[UCAL_DAY_OF_WEEK
] - fStamp
[UCAL_DATE
];
2644 if ( monthStampDelta
>= 1 && monthStampDelta
<= 3 && dateStampDelta
>= 1 && dateStampDelta
<= 3 ) {
2645 // If UCAL_MONTH, UCAL_DATE and UCAL_DAY_OF_WEEK are all explicitly set nearly one after the
2646 // other (as when parsing a single date format), with UCAL_DAY_OF_WEEK set most recently, and
2647 // if UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, and UCAL_DAY_OF_WEEK_IN_MONTH are all only
2648 // implicitly set (as from setTimeInMillis), then for the calculations in this call temporarily
2649 // treat UCAL_WEEK_OF_YEAR, UCAL_WEEK_OF_MONTH, and UCAL_DAY_OF_WEEK_IN_MONTH as unset so they
2650 // don't combine with UCAL_DAY_OF_WEEK to override the date in UCAL_MONTH & UCAL_DATE. All of
2651 // these conditions are to avoid messing up the case of parsing a format with UCAL_DAY_OF_WEEK
2652 // alone or in combination with other fields besides UCAL_MONTH, UCAL_DATE. Note: the current
2653 // stamp value is incremented each time Calendar::set is called to explicitly set a field value.
2654 fStamp
[UCAL_WEEK_OF_YEAR
] = kUnset
;
2655 fStamp
[UCAL_WEEK_OF_MONTH
] = kUnset
;
2656 fStamp
[UCAL_DAY_OF_WEEK_IN_MONTH
] = kUnset
;
2657 restoreWeekOfInternalSet
= TRUE
;
2660 for (int32_t g
=0; precedenceTable
[g
][0][0] != -1 && (bestField
== UCAL_FIELD_COUNT
); ++g
) {
2661 int32_t bestStamp
= kUnset
;
2662 for (int32_t l
=0; precedenceTable
[g
][l
][0] != -1; ++l
) {
2663 int32_t lineStamp
= kUnset
;
2664 // Skip over first entry if it is negative
2665 for (int32_t i
=((precedenceTable
[g
][l
][0]>=kResolveRemap
)?1:0); precedenceTable
[g
][l
][i
]!=-1; ++i
) {
2666 U_ASSERT(precedenceTable
[g
][l
][i
] < UCAL_FIELD_COUNT
);
2667 int32_t s
= fStamp
[precedenceTable
[g
][l
][i
]];
2668 // If any field is unset then don't use this line
2671 } else if(s
> lineStamp
) {
2675 // Record new maximum stamp & field no.
2676 if (lineStamp
> bestStamp
) {
2677 tempBestField
= precedenceTable
[g
][l
][0]; // First field refers to entire line
2678 if (tempBestField
>= kResolveRemap
) {
2679 tempBestField
&= (kResolveRemap
-1);
2680 // This check is needed to resolve some issues with UCAL_YEAR precedence mapping
2681 if (tempBestField
!= UCAL_DATE
|| (fStamp
[UCAL_WEEK_OF_MONTH
] < fStamp
[tempBestField
])) {
2682 bestField
= tempBestField
;
2685 bestField
= tempBestField
;
2688 if (bestField
== tempBestField
) {
2689 bestStamp
= lineStamp
;
2696 if (restoreWeekOfInternalSet
) {
2697 // Restore the field stamps temporarily unset above.
2698 fStamp
[UCAL_WEEK_OF_YEAR
] = kInternallySet
;
2699 fStamp
[UCAL_WEEK_OF_MONTH
] = kInternallySet
;
2700 fStamp
[UCAL_DAY_OF_WEEK_IN_MONTH
] = kInternallySet
;
2702 return (UCalendarDateFields
)bestField
;
2705 const UFieldResolutionTable
Calendar::kDatePrecedence
[] =
2708 { UCAL_DAY_OF_MONTH
, kResolveSTOP
},
2709 { UCAL_WEEK_OF_YEAR
, UCAL_DAY_OF_WEEK
, kResolveSTOP
},
2710 { UCAL_WEEK_OF_MONTH
, UCAL_DAY_OF_WEEK
, kResolveSTOP
},
2711 { UCAL_DAY_OF_WEEK_IN_MONTH
, UCAL_DAY_OF_WEEK
, kResolveSTOP
},
2712 { UCAL_WEEK_OF_YEAR
, UCAL_DOW_LOCAL
, kResolveSTOP
},
2713 { UCAL_WEEK_OF_MONTH
, UCAL_DOW_LOCAL
, kResolveSTOP
},
2714 { UCAL_DAY_OF_WEEK_IN_MONTH
, UCAL_DOW_LOCAL
, kResolveSTOP
},
2715 { UCAL_DAY_OF_YEAR
, kResolveSTOP
},
2716 { kResolveRemap
| UCAL_DAY_OF_MONTH
, UCAL_YEAR
, kResolveSTOP
}, // if YEAR is set over YEAR_WOY use DAY_OF_MONTH
2717 { kResolveRemap
| UCAL_WEEK_OF_YEAR
, UCAL_YEAR_WOY
, kResolveSTOP
}, // if YEAR_WOY is set, calc based on WEEK_OF_YEAR
2721 { UCAL_WEEK_OF_YEAR
, kResolveSTOP
},
2722 { UCAL_WEEK_OF_MONTH
, kResolveSTOP
},
2723 { UCAL_DAY_OF_WEEK_IN_MONTH
, kResolveSTOP
},
2724 { kResolveRemap
| UCAL_DAY_OF_WEEK_IN_MONTH
, UCAL_DAY_OF_WEEK
, kResolveSTOP
},
2725 { kResolveRemap
| UCAL_DAY_OF_WEEK_IN_MONTH
, UCAL_DOW_LOCAL
, kResolveSTOP
},
2732 const UFieldResolutionTable
Calendar::kDOWPrecedence
[] =
2735 { UCAL_DAY_OF_WEEK
,kResolveSTOP
, kResolveSTOP
},
2736 { UCAL_DOW_LOCAL
,kResolveSTOP
, kResolveSTOP
},
2742 // precedence for calculating a year
2743 const UFieldResolutionTable
Calendar::kYearPrecedence
[] =
2746 { UCAL_YEAR
, kResolveSTOP
},
2747 { UCAL_EXTENDED_YEAR
, kResolveSTOP
},
2748 { UCAL_YEAR_WOY
, UCAL_WEEK_OF_YEAR
, kResolveSTOP
}, // YEAR_WOY is useless without WEEK_OF_YEAR
2755 // -------------------------
2758 void Calendar::computeTime(UErrorCode
& status
) {
2760 validateFields(status
);
2761 if (U_FAILURE(status
)) {
2766 // Compute the Julian day
2767 int32_t julianDay
= computeJulianDay();
2769 double millis
= Grego::julianDayToMillis(julianDay
);
2771 #if defined (U_DEBUG_CAL)
2772 // int32_t julianInsanityCheck = (int32_t)ClockMath::floorDivide(millis, kOneDay);
2773 // julianInsanityCheck += kEpochStartAsJulianDay;
2774 // if(1 || julianInsanityCheck != julianDay) {
2775 // fprintf(stderr, "%s:%d- D'oh- computed jules %d, to mills (%s)%.lf, recomputed %d\n",
2776 // __FILE__, __LINE__, julianDay, millis<0.0?"NEG":"", millis, julianInsanityCheck);
2780 int32_t millisInDay
;
2782 // We only use MILLISECONDS_IN_DAY if it has been set by the user.
2783 // This makes it possible for the caller to set the calendar to a
2784 // time and call clear(MONTH) to reset the MONTH to January. This
2785 // is legacy behavior. Without this, clear(MONTH) has no effect,
2786 // since the internally set JULIAN_DAY is used.
2787 if (fStamp
[UCAL_MILLISECONDS_IN_DAY
] >= ((int32_t)kMinimumUserStamp
) &&
2788 newestStamp(UCAL_AM_PM
, UCAL_MILLISECOND
, kUnset
) <= fStamp
[UCAL_MILLISECONDS_IN_DAY
]) {
2789 millisInDay
= internalGet(UCAL_MILLISECONDS_IN_DAY
);
2791 millisInDay
= computeMillisInDay();
2795 if (fStamp
[UCAL_ZONE_OFFSET
] >= ((int32_t)kMinimumUserStamp
) || fStamp
[UCAL_DST_OFFSET
] >= ((int32_t)kMinimumUserStamp
)) {
2796 t
= millis
+ millisInDay
- (internalGet(UCAL_ZONE_OFFSET
) + internalGet(UCAL_DST_OFFSET
));
2798 // Compute the time zone offset and DST offset. There are two potential
2799 // ambiguities here. We'll assume a 2:00 am (wall time) switchover time
2800 // for discussion purposes here.
2802 // 1. The positive offset change such as transition into DST.
2803 // Here, a designated time of 2:00 am - 2:59 am does not actually exist.
2804 // For this case, skippedWallTime option specifies the behavior.
2805 // For example, 2:30 am is interpreted as;
2806 // - WALLTIME_LAST(default): 3:30 am (DST) (interpreting 2:30 am as 31 minutes after 1:59 am (STD))
2807 // - WALLTIME_FIRST: 1:30 am (STD) (interpreting 2:30 am as 30 minutes before 3:00 am (DST))
2808 // - WALLTIME_NEXT_VALID: 3:00 am (DST) (next valid time after 2:30 am on a wall clock)
2809 // 2. The negative offset change such as transition out of DST.
2810 // Here, a designated time of 1:00 am - 1:59 am can be in standard or DST. Both are valid
2811 // representations (the rep jumps from 1:59:59 DST to 1:00:00 Std).
2812 // For this case, repeatedWallTime option specifies the behavior.
2813 // For example, 1:30 am is interpreted as;
2814 // - WALLTIME_LAST(default): 1:30 am (STD) - latter occurrence
2815 // - WALLTIME_FIRST: 1:30 am (DST) - former occurrence
2817 // In addition to above, when calendar is strict (not default), wall time falls into
2818 // the skipped time range will be processed as an error case.
2820 // These special cases are mostly handled in #computeZoneOffset(long), except WALLTIME_NEXT_VALID
2821 // at positive offset change. The protected method computeZoneOffset(long) is exposed to Calendar
2822 // subclass implementations and marked as @stable. Strictly speaking, WALLTIME_NEXT_VALID
2823 // should be also handled in the same place, but we cannot change the code flow without deprecating
2824 // the protected method.
2826 // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
2827 // or DST_OFFSET fields; then we use those fields.
2829 if (!isLenient() || fSkippedWallTime
== UCAL_WALLTIME_NEXT_VALID
) {
2830 // When strict, invalidate a wall time falls into a skipped wall time range.
2831 // When lenient and skipped wall time option is WALLTIME_NEXT_VALID,
2832 // the result time will be adjusted to the next valid time (on wall clock).
2833 int32_t zoneOffset
= computeZoneOffset(millis
, millisInDay
, status
);
2834 UDate tmpTime
= millis
+ millisInDay
- zoneOffset
;
2837 fZone
->getOffset(tmpTime
, FALSE
, raw
, dst
, status
);
2839 if (U_SUCCESS(status
)) {
2840 // zoneOffset != (raw + dst) only when the given wall time fall into
2841 // a skipped wall time range caused by positive zone offset transition.
2842 if (zoneOffset
!= (raw
+ dst
)) {
2844 status
= U_ILLEGAL_ARGUMENT_ERROR
;
2846 U_ASSERT(fSkippedWallTime
== UCAL_WALLTIME_NEXT_VALID
);
2847 // Adjust time to the next valid wall clock time.
2848 // At this point, tmpTime is on or after the zone offset transition causing
2849 // the skipped time range.
2851 BasicTimeZone
*btz
= getBasicTimeZone();
2853 TimeZoneTransition transition
;
2854 UBool hasTransition
= btz
->getPreviousTransition(tmpTime
, TRUE
, transition
);
2855 if (hasTransition
) {
2856 t
= transition
.getTime();
2858 // Could not find any transitions.
2859 // Note: This should never happen.
2860 status
= U_INTERNAL_PROGRAM_ERROR
;
2863 // If not BasicTimeZone, return unsupported error for now.
2864 // TODO: We may support non-BasicTimeZone in future.
2865 status
= U_UNSUPPORTED_ERROR
;
2873 t
= millis
+ millisInDay
- computeZoneOffset(millis
, millisInDay
, status
);
2876 if (U_SUCCESS(status
)) {
2882 * Compute the milliseconds in the day from the fields. This is a
2883 * value from 0 to 23:59:59.999 inclusive, unless fields are out of
2884 * range, in which case it can be an arbitrary value. This value
2885 * reflects local zone wall time.
2888 int32_t Calendar::computeMillisInDay() {
2889 // Do the time portion of the conversion.
2891 int32_t millisInDay
= 0;
2893 // Find the best set of fields specifying the time of day. There
2894 // are only two possibilities here; the HOUR_OF_DAY or the
2895 // AM_PM and the HOUR.
2896 int32_t hourOfDayStamp
= fStamp
[UCAL_HOUR_OF_DAY
];
2897 int32_t hourStamp
= (fStamp
[UCAL_HOUR
] > fStamp
[UCAL_AM_PM
])?fStamp
[UCAL_HOUR
]:fStamp
[UCAL_AM_PM
];
2898 int32_t bestStamp
= (hourStamp
> hourOfDayStamp
) ? hourStamp
: hourOfDayStamp
;
2901 if (bestStamp
!= kUnset
) {
2902 if (bestStamp
== hourOfDayStamp
) {
2903 // Don't normalize here; let overflow bump into the next period.
2904 // This is consistent with how we handle other fields.
2905 millisInDay
+= internalGet(UCAL_HOUR_OF_DAY
);
2907 // Don't normalize here; let overflow bump into the next period.
2908 // This is consistent with how we handle other fields.
2909 millisInDay
+= internalGet(UCAL_HOUR
);
2910 millisInDay
+= 12 * internalGet(UCAL_AM_PM
); // Default works for unset AM_PM
2914 // We use the fact that unset == 0; we start with millisInDay
2917 millisInDay
+= internalGet(UCAL_MINUTE
); // now have minutes
2919 millisInDay
+= internalGet(UCAL_SECOND
); // now have seconds
2920 millisInDay
*= 1000;
2921 millisInDay
+= internalGet(UCAL_MILLISECOND
); // now have millis
2927 * This method can assume EXTENDED_YEAR has been set.
2928 * @param millis milliseconds of the date fields
2929 * @param millisInDay milliseconds of the time fields; may be out
2933 int32_t Calendar::computeZoneOffset(double millis
, int32_t millisInDay
, UErrorCode
&ec
) {
2934 int32_t rawOffset
, dstOffset
;
2935 UDate wall
= millis
+ millisInDay
;
2936 BasicTimeZone
* btz
= getBasicTimeZone();
2938 int duplicatedTimeOpt
= (fRepeatedWallTime
== UCAL_WALLTIME_FIRST
) ? BasicTimeZone::kFormer
: BasicTimeZone::kLatter
;
2939 int nonExistingTimeOpt
= (fSkippedWallTime
== UCAL_WALLTIME_FIRST
) ? BasicTimeZone::kLatter
: BasicTimeZone::kFormer
;
2940 btz
->getOffsetFromLocal(wall
, nonExistingTimeOpt
, duplicatedTimeOpt
, rawOffset
, dstOffset
, ec
);
2942 const TimeZone
& tz
= getTimeZone();
2943 // By default, TimeZone::getOffset behaves UCAL_WALLTIME_LAST for both.
2944 tz
.getOffset(wall
, TRUE
, rawOffset
, dstOffset
, ec
);
2946 UBool sawRecentNegativeShift
= FALSE
;
2947 if (fRepeatedWallTime
== UCAL_WALLTIME_FIRST
) {
2948 // Check if the given wall time falls into repeated time range
2949 UDate tgmt
= wall
- (rawOffset
+ dstOffset
);
2951 // Any negative zone transition within last 6 hours?
2952 // Note: The maximum historic negative zone transition is -3 hours in the tz database.
2953 // 6 hour window would be sufficient for this purpose.
2954 int32_t tmpRaw
, tmpDst
;
2955 tz
.getOffset(tgmt
- 6*60*60*1000, FALSE
, tmpRaw
, tmpDst
, ec
);
2956 int32_t offsetDelta
= (rawOffset
+ dstOffset
) - (tmpRaw
+ tmpDst
);
2958 U_ASSERT(offsetDelta
< -6*60*60*1000);
2959 if (offsetDelta
< 0) {
2960 sawRecentNegativeShift
= TRUE
;
2961 // Negative shift within last 6 hours. When UCAL_WALLTIME_FIRST is used and the given wall time falls
2962 // into the repeated time range, use offsets before the transition.
2963 // Note: If it does not fall into the repeated time range, offsets remain unchanged below.
2964 tz
.getOffset(wall
+ offsetDelta
, TRUE
, rawOffset
, dstOffset
, ec
);
2967 if (!sawRecentNegativeShift
&& fSkippedWallTime
== UCAL_WALLTIME_FIRST
) {
2968 // When skipped wall time option is WALLTIME_FIRST,
2969 // recalculate offsets from the resolved time (non-wall).
2970 // When the given wall time falls into skipped wall time,
2971 // the offsets will be based on the zone offsets AFTER
2972 // the transition (which means, earliest possibe interpretation).
2973 UDate tgmt
= wall
- (rawOffset
+ dstOffset
);
2974 tz
.getOffset(tgmt
, FALSE
, rawOffset
, dstOffset
, ec
);
2977 return rawOffset
+ dstOffset
;
2980 int32_t Calendar::computeJulianDay()
2982 // We want to see if any of the date fields is newer than the
2983 // JULIAN_DAY. If not, then we use JULIAN_DAY. If so, then we do
2984 // the normal resolution. We only use JULIAN_DAY if it has been
2985 // set by the user. This makes it possible for the caller to set
2986 // the calendar to a time and call clear(MONTH) to reset the MONTH
2987 // to January. This is legacy behavior. Without this,
2988 // clear(MONTH) has no effect, since the internally set JULIAN_DAY
2990 if (fStamp
[UCAL_JULIAN_DAY
] >= (int32_t)kMinimumUserStamp
) {
2991 int32_t bestStamp
= newestStamp(UCAL_ERA
, UCAL_DAY_OF_WEEK_IN_MONTH
, kUnset
);
2992 bestStamp
= newestStamp(UCAL_YEAR_WOY
, UCAL_EXTENDED_YEAR
, bestStamp
);
2993 if (bestStamp
<= fStamp
[UCAL_JULIAN_DAY
]) {
2994 return internalGet(UCAL_JULIAN_DAY
);
2998 UCalendarDateFields bestField
= resolveFields(getFieldResolutionTable());
2999 if (bestField
== UCAL_FIELD_COUNT
) {
3000 bestField
= UCAL_DAY_OF_MONTH
;
3003 return handleComputeJulianDay(bestField
);
3006 // -------------------------------------------
3008 int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField
) {
3009 UBool useMonth
= (bestField
== UCAL_DAY_OF_MONTH
||
3010 bestField
== UCAL_WEEK_OF_MONTH
||
3011 bestField
== UCAL_DAY_OF_WEEK_IN_MONTH
);
3014 if (bestField
== UCAL_WEEK_OF_YEAR
) {
3015 year
= internalGet(UCAL_YEAR_WOY
, handleGetExtendedYear());
3016 internalSet(UCAL_EXTENDED_YEAR
, year
);
3018 year
= handleGetExtendedYear();
3019 internalSet(UCAL_EXTENDED_YEAR
, year
);
3022 #if defined (U_DEBUG_CAL)
3023 fprintf(stderr
, "%s:%d: bestField= %s - y=%d\n", __FILE__
, __LINE__
, fldName(bestField
), year
);
3026 // Get the Julian day of the day BEFORE the start of this year.
3027 // If useMonth is true, get the day before the start of the month.
3029 // give calendar subclass a chance to have a default 'first' month
3032 if(isSet(UCAL_MONTH
)) {
3033 month
= internalGet(UCAL_MONTH
);
3035 month
= getDefaultMonthInYear(year
);
3038 int32_t julianDay
= handleComputeMonthStart(year
, useMonth
? month
: 0, useMonth
);
3040 if (bestField
== UCAL_DAY_OF_MONTH
) {
3042 // give calendar subclass a chance to have a default 'first' dom
3044 if(isSet(UCAL_DAY_OF_MONTH
)) {
3045 dayOfMonth
= internalGet(UCAL_DAY_OF_MONTH
,1);
3047 dayOfMonth
= getDefaultDayInMonth(year
, month
);
3049 return julianDay
+ dayOfMonth
;
3052 if (bestField
== UCAL_DAY_OF_YEAR
) {
3053 return julianDay
+ internalGet(UCAL_DAY_OF_YEAR
);
3056 int32_t firstDayOfWeek
= getFirstDayOfWeek(); // Localized fdw
3058 // At this point julianDay is the 0-based day BEFORE the first day of
3059 // January 1, year 1 of the given calendar. If julianDay == 0, it
3060 // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
3061 // or Gregorian). (or it is before the month we are in, if useMonth is True)
3063 // At this point we need to process the WEEK_OF_MONTH or
3064 // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
3065 // First, perform initial shared computations. These locate the
3066 // first week of the period.
3068 // Get the 0-based localized DOW of day one of the month or year.
3069 // Valid range 0..6.
3070 int32_t first
= julianDayToDayOfWeek(julianDay
+ 1) - firstDayOfWeek
;
3075 int32_t dowLocal
= getLocalDOW();
3077 // Find the first target DOW (dowLocal) in the month or year.
3078 // Actually, it may be just before the first of the month or year.
3079 // It will be an integer from -5..7.
3080 int32_t date
= 1 - first
+ dowLocal
;
3082 if (bestField
== UCAL_DAY_OF_WEEK_IN_MONTH
) {
3083 // Adjust the target DOW to be in the month or year.
3088 // The only trickiness occurs if the day-of-week-in-month is
3090 int32_t dim
= internalGet(UCAL_DAY_OF_WEEK_IN_MONTH
, 1);
3092 date
+= 7*(dim
- 1);
3095 // Move date to the last of this day-of-week in this month,
3096 // then back up as needed. If dim==-1, we don't back up at
3097 // all. If dim==-2, we back up once, etc. Don't back up
3098 // past the first of the given day-of-week in this month.
3099 // Note that we handle -2, -3, etc. correctly, even though
3100 // values < -1 are technically disallowed.
3101 int32_t m
= internalGet(UCAL_MONTH
, UCAL_JANUARY
);
3102 int32_t monthLength
= handleGetMonthLength(year
, m
);
3103 date
+= ((monthLength
- date
) / 7 + dim
+ 1) * 7;
3106 #if defined (U_DEBUG_CAL)
3107 fprintf(stderr
, "%s:%d - bf= %s\n", __FILE__
, __LINE__
, fldName(bestField
));
3110 if(bestField
== UCAL_WEEK_OF_YEAR
) { // ------------------------------------- WOY -------------
3111 if(!isSet(UCAL_YEAR_WOY
) || // YWOY not set at all or
3112 ( (resolveFields(kYearPrecedence
) != UCAL_YEAR_WOY
) // YWOY doesn't have precedence
3113 && (fStamp
[UCAL_YEAR_WOY
]!=kInternallySet
) ) ) // (excluding where all fields are internally set - then YWOY is used)
3115 // need to be sure to stay in 'real' year.
3116 int32_t woy
= internalGet(bestField
);
3118 int32_t nextJulianDay
= handleComputeMonthStart(year
+1, 0, FALSE
); // jd of day before jan 1
3119 int32_t nextFirst
= julianDayToDayOfWeek(nextJulianDay
+ 1) - firstDayOfWeek
;
3121 if (nextFirst
< 0) { // 0..6 ldow of Jan 1
3125 if(woy
==1) { // FIRST WEEK ---------------------------------
3126 #if defined (U_DEBUG_CAL)
3127 fprintf(stderr
, "%s:%d - woy=%d, yp=%d, nj(%d)=%d, nf=%d", __FILE__
, __LINE__
,
3128 internalGet(bestField
), resolveFields(kYearPrecedence
), year
+1,
3129 nextJulianDay
, nextFirst
);
3131 fprintf(stderr
, " next: %d DFW, min=%d \n", (7-nextFirst
), getMinimalDaysInFirstWeek() );
3134 // nextFirst is now the localized DOW of Jan 1 of y-woy+1
3135 if((nextFirst
> 0) && // Jan 1 starts on FDOW
3136 (7-nextFirst
) >= getMinimalDaysInFirstWeek()) // or enough days in the week
3138 // Jan 1 of (yearWoy+1) is in yearWoy+1 - recalculate JD to next year
3139 #if defined (U_DEBUG_CAL)
3140 fprintf(stderr
, "%s:%d - was going to move JD from %d to %d [d%d]\n", __FILE__
, __LINE__
,
3141 julianDay
, nextJulianDay
, (nextJulianDay
-julianDay
));
3143 julianDay
= nextJulianDay
;
3145 // recalculate 'first' [0-based local dow of jan 1]
3146 first
= julianDayToDayOfWeek(julianDay
+ 1) - firstDayOfWeek
;
3150 // recalculate date.
3151 date
= 1 - first
+ dowLocal
;
3153 } else if(woy
>=getLeastMaximum(bestField
)) {
3154 // could be in the last week- find out if this JD would overstep
3155 int32_t testDate
= date
;
3156 if ((7 - first
) < getMinimalDaysInFirstWeek()) {
3160 // Now adjust for the week number.
3161 testDate
+= 7 * (woy
- 1);
3163 #if defined (U_DEBUG_CAL)
3164 fprintf(stderr
, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n",
3165 __FILE__
, __LINE__
, year
, year
-1, testDate
, julianDay
+testDate
, nextJulianDay
);
3167 if(julianDay
+testDate
> nextJulianDay
) { // is it past Dec 31? (nextJulianDay is day BEFORE year+1's Jan 1)
3168 // Fire up the calculating engines.. retry YWOY = (year-1)
3169 julianDay
= handleComputeMonthStart(year
-1, 0, FALSE
); // jd before Jan 1 of previous year
3170 first
= julianDayToDayOfWeek(julianDay
+ 1) - firstDayOfWeek
; // 0 based local dow of first week
3172 if(first
< 0) { // 0..6
3175 date
= 1 - first
+ dowLocal
;
3177 #if defined (U_DEBUG_CAL)
3178 fprintf(stderr
, "%s:%d - date now %d, jd%d, ywoy%d\n",
3179 __FILE__
, __LINE__
, date
, julianDay
, year
-1);
3183 } /* correction needed */
3184 } /* leastmaximum */
3185 } /* resolvefields(year) != year_woy */
3186 } /* bestfield != week_of_year */
3188 // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR)
3189 // Adjust for minimal days in first week
3190 if ((7 - first
) < getMinimalDaysInFirstWeek()) {
3194 // Now adjust for the week number.
3195 date
+= 7 * (internalGet(bestField
) - 1);
3198 return julianDay
+ date
;
3202 Calendar::getDefaultMonthInYear(int32_t /*eyear*/)
3208 Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/)
3214 int32_t Calendar::getLocalDOW()
3216 // Get zero-based localized DOW, valid range 0..6. This is the DOW
3217 // we are looking for.
3218 int32_t dowLocal
= 0;
3219 switch (resolveFields(kDOWPrecedence
)) {
3220 case UCAL_DAY_OF_WEEK
:
3221 dowLocal
= internalGet(UCAL_DAY_OF_WEEK
) - fFirstDayOfWeek
;
3223 case UCAL_DOW_LOCAL
:
3224 dowLocal
= internalGet(UCAL_DOW_LOCAL
) - 1;
3229 dowLocal
= dowLocal
% 7;
3236 int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy
, int32_t woy
)
3238 // We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine
3239 // what year we fall in, so that other code can set it properly.
3240 // (code borrowed from computeWeekFields and handleComputeJulianDay)
3243 // First, we need a reliable DOW.
3244 UCalendarDateFields bestField
= resolveFields(kDatePrecedence
); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields
3247 int32_t dowLocal
= getLocalDOW(); // 0..6
3248 int32_t firstDayOfWeek
= getFirstDayOfWeek(); // Localized fdw
3249 int32_t jan1Start
= handleComputeMonthStart(yearWoy
, 0, FALSE
);
3250 int32_t nextJan1Start
= handleComputeMonthStart(yearWoy
+1, 0, FALSE
); // next year's Jan1 start
3252 // At this point julianDay is the 0-based day BEFORE the first day of
3253 // January 1, year 1 of the given calendar. If julianDay == 0, it
3254 // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
3255 // or Gregorian). (or it is before the month we are in, if useMonth is True)
3257 // At this point we need to process the WEEK_OF_MONTH or
3258 // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
3259 // First, perform initial shared computations. These locate the
3260 // first week of the period.
3262 // Get the 0-based localized DOW of day one of the month or year.
3263 // Valid range 0..6.
3264 int32_t first
= julianDayToDayOfWeek(jan1Start
+ 1) - firstDayOfWeek
;
3268 int32_t nextFirst
= julianDayToDayOfWeek(nextJan1Start
+ 1) - firstDayOfWeek
;
3269 if (nextFirst
< 0) {
3273 int32_t minDays
= getMinimalDaysInFirstWeek();
3274 UBool jan1InPrevYear
= FALSE
; // January 1st in the year of WOY is the 1st week? (i.e. first week is < minimal )
3275 //UBool nextJan1InPrevYear = FALSE; // January 1st of Year of WOY + 1 is in the first week?
3277 if((7 - first
) < minDays
) {
3278 jan1InPrevYear
= TRUE
;
3281 // if((7 - nextFirst) < minDays) {
3282 // nextJan1InPrevYear = TRUE;
3286 case UCAL_WEEK_OF_YEAR
:
3288 if(jan1InPrevYear
== TRUE
) {
3289 // the first week of January is in the previous year
3290 // therefore WOY1 is always solidly within yearWoy
3293 // First WOY is split between two years
3294 if( dowLocal
< first
) { // we are prior to Jan 1
3295 return yearWoy
-1; // previous year
3297 return yearWoy
; // in this year
3300 } else if(woy
>= getLeastMaximum(bestField
)) {
3301 // we _might_ be in the last week..
3302 int32_t jd
= // Calculate JD of our target day:
3303 jan1Start
+ // JD of Jan 1
3304 (7-first
) + // days in the first week (Jan 1.. )
3305 (woy
-1)*7 + // add the weeks of the year
3306 dowLocal
; // the local dow (0..6) of last week
3307 if(jan1InPrevYear
==FALSE
) {
3308 jd
-= 7; // woy already includes Jan 1's week.
3311 if( (jd
+1) >= nextJan1Start
) {
3312 // we are in week 52 or 53 etc. - actual year is yearWoy+1
3315 // still in yearWoy;
3319 // we're not possibly in the last week -must be ywoy
3324 if((internalGet(UCAL_MONTH
)==0) &&
3325 (woy
>= getLeastMaximum(UCAL_WEEK_OF_YEAR
))) {
3326 return yearWoy
+1; // month 0, late woy = in the next year
3328 //if(nextJan1InPrevYear) {
3329 if(internalGet(UCAL_MONTH
)==0) {
3337 //(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow */ ) {
3338 //within 1st week and in this month..
3342 default: // assume the year is appropriate
3347 int32_t Calendar::handleGetMonthLength(int32_t extendedYear
, int32_t month
) const
3349 return handleComputeMonthStart(extendedYear
, month
+1, TRUE
) -
3350 handleComputeMonthStart(extendedYear
, month
, TRUE
);
3353 int32_t Calendar::handleGetYearLength(int32_t eyear
) const {
3354 return handleComputeMonthStart(eyear
+1, 0, FALSE
) -
3355 handleComputeMonthStart(eyear
, 0, FALSE
);
3359 Calendar::getActualMaximum(UCalendarDateFields field
, UErrorCode
& status
) const
3365 if(U_FAILURE(status
)) return 0;
3366 Calendar
*cal
= clone();
3367 if(!cal
) { status
= U_MEMORY_ALLOCATION_ERROR
; return 0; }
3368 cal
->setLenient(TRUE
);
3369 cal
->prepareGetActual(field
,FALSE
,status
);
3370 result
= handleGetMonthLength(cal
->get(UCAL_EXTENDED_YEAR
, status
), cal
->get(UCAL_MONTH
, status
));
3375 case UCAL_DAY_OF_YEAR
:
3377 if(U_FAILURE(status
)) return 0;
3378 Calendar
*cal
= clone();
3379 if(!cal
) { status
= U_MEMORY_ALLOCATION_ERROR
; return 0; }
3380 cal
->setLenient(TRUE
);
3381 cal
->prepareGetActual(field
,FALSE
,status
);
3382 result
= handleGetYearLength(cal
->get(UCAL_EXTENDED_YEAR
, status
));
3387 case UCAL_DAY_OF_WEEK
:
3390 case UCAL_HOUR_OF_DAY
:
3393 case UCAL_MILLISECOND
:
3394 case UCAL_ZONE_OFFSET
:
3395 case UCAL_DST_OFFSET
:
3396 case UCAL_DOW_LOCAL
:
3397 case UCAL_JULIAN_DAY
:
3398 case UCAL_MILLISECONDS_IN_DAY
:
3399 // These fields all have fixed minima/maxima
3400 result
= getMaximum(field
);
3404 // For all other fields, do it the hard way....
3405 result
= getActualHelper(field
, getLeastMaximum(field
), getMaximum(field
),status
);
3413 * Prepare this calendar for computing the actual minimum or maximum.
3414 * This method modifies this calendar's fields; it is called on a
3415 * temporary calendar.
3417 * <p>Rationale: The semantics of getActualXxx() is to return the
3418 * maximum or minimum value that the given field can take, taking into
3419 * account other relevant fields. In general these other fields are
3420 * larger fields. For example, when computing the actual maximum
3421 * DATE, the current value of DATE itself is ignored,
3422 * as is the value of any field smaller.
3424 * <p>The time fields all have fixed minima and maxima, so we don't
3425 * need to worry about them. This also lets us set the
3426 * MILLISECONDS_IN_DAY to zero to erase any effects the time fields
3427 * might have when computing date fields.
3429 * <p>DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and
3430 * WEEK_OF_YEAR fields to ensure that they are computed correctly.
3433 void Calendar::prepareGetActual(UCalendarDateFields field
, UBool isMinimum
, UErrorCode
&status
)
3435 set(UCAL_MILLISECONDS_IN_DAY
, 0);
3439 case UCAL_EXTENDED_YEAR
:
3440 set(UCAL_DAY_OF_YEAR
, getGreatestMinimum(UCAL_DAY_OF_YEAR
));
3444 set(UCAL_WEEK_OF_YEAR
, getGreatestMinimum(UCAL_WEEK_OF_YEAR
));
3447 set(UCAL_DATE
, getGreatestMinimum(UCAL_DATE
));
3450 case UCAL_DAY_OF_WEEK_IN_MONTH
:
3451 // For dowim, the maximum occurs for the DOW of the first of the
3454 set(UCAL_DAY_OF_WEEK
, get(UCAL_DAY_OF_WEEK
, status
)); // Make this user set
3457 case UCAL_WEEK_OF_MONTH
:
3458 case UCAL_WEEK_OF_YEAR
:
3459 // If we're counting weeks, set the day of the week to either the
3460 // first or last localized DOW. We know the last week of a month
3461 // or year will contain the first day of the week, and that the
3462 // first week will contain the last DOW.
3464 int32_t dow
= fFirstDayOfWeek
;
3466 dow
= (dow
+ 6) % 7; // set to last DOW
3467 if (dow
< UCAL_SUNDAY
) {
3471 #if defined (U_DEBUG_CAL)
3472 fprintf(stderr
, "prepareGetActualHelper(WOM/WOY) - dow=%d\n", dow
);
3474 set(UCAL_DAY_OF_WEEK
, dow
);
3481 // Do this last to give it the newest time stamp
3482 set(field
, getGreatestMinimum(field
));
3485 int32_t Calendar::getActualHelper(UCalendarDateFields field
, int32_t startValue
, int32_t endValue
, UErrorCode
&status
) const
3487 #if defined (U_DEBUG_CAL)
3488 fprintf(stderr
, "getActualHelper(%d,%d .. %d, %s)\n", field
, startValue
, endValue
, u_errorName(status
));
3490 if (startValue
== endValue
) {
3491 // if we know that the maximum value is always the same, just return it
3495 int32_t delta
= (endValue
> startValue
) ? 1 : -1;
3497 // clone the calendar so we don't mess with the real one, and set it to
3498 // accept anything for the field values
3499 if(U_FAILURE(status
)) return startValue
;
3500 Calendar
*work
= clone();
3501 if(!work
) { status
= U_MEMORY_ALLOCATION_ERROR
; return startValue
; }
3503 // need to resolve time here, otherwise, fields set for actual limit
3504 // may cause conflict with fields previously set (but not yet resolved).
3505 work
->complete(status
);
3507 work
->setLenient(TRUE
);
3508 work
->prepareGetActual(field
, delta
< 0, status
);
3510 // now try each value from the start to the end one by one until
3511 // we get a value that normalizes to another value. The last value that
3512 // normalizes to itself is the actual maximum for the current date
3513 work
->set(field
, startValue
);
3515 // prepareGetActual sets the first day of week in the same week with
3516 // the first day of a month. Unlike WEEK_OF_YEAR, week number for the
3517 // week which contains days from both previous and current month is
3518 // not unique. For example, last several days in the previous month
3519 // is week 5, and the rest of week is week 1.
3520 int32_t result
= startValue
;
3521 if ((work
->get(field
, status
) != startValue
3522 && field
!= UCAL_WEEK_OF_MONTH
&& delta
> 0 ) || U_FAILURE(status
)) {
3523 #if defined (U_DEBUG_CAL)
3524 fprintf(stderr
, "getActualHelper(fld %d) - got %d (not %d) - %s\n", field
, work
->get(field
,status
), startValue
, u_errorName(status
));
3528 startValue
+= delta
;
3529 work
->add(field
, delta
, status
);
3530 if (work
->get(field
, status
) != startValue
|| U_FAILURE(status
)) {
3531 #if defined (U_DEBUG_CAL)
3532 fprintf(stderr
, "getActualHelper(fld %d) - got %d (not %d), BREAK - %s\n", field
, work
->get(field
,status
), startValue
, u_errorName(status
));
3536 result
= startValue
;
3537 } while (startValue
!= endValue
);
3540 #if defined (U_DEBUG_CAL)
3541 fprintf(stderr
, "getActualHelper(%d) = %d\n", field
, result
);
3549 // -------------------------------------
3552 Calendar::setWeekData(const Locale
& desiredLocale
, const char *type
, UErrorCode
& status
)
3555 if (U_FAILURE(status
)) return;
3557 fFirstDayOfWeek
= UCAL_SUNDAY
;
3558 fMinimalDaysInFirstWeek
= 1;
3559 fWeekendOnset
= UCAL_SATURDAY
;
3560 fWeekendOnsetMillis
= 0;
3561 fWeekendCease
= UCAL_SUNDAY
;
3562 fWeekendCeaseMillis
= 86400000; // 24*60*60*1000
3564 // Since week and weekend data is territory based instead of language based,
3565 // we may need to tweak the locale that we are using to try to get the appropriate
3566 // values, using the following logic:
3567 // 1). If the locale has a language but no territory, use the territory as defined by
3568 // the likely subtags.
3569 // 2). If the locale has a script designation then we ignore it,
3570 // then remove it ( i.e. "en_Latn_US" becomes "en_US" )
3572 char minLocaleID
[ULOC_FULLNAME_CAPACITY
] = { 0 };
3573 UErrorCode myStatus
= U_ZERO_ERROR
;
3575 uloc_minimizeSubtags(desiredLocale
.getName(),minLocaleID
,ULOC_FULLNAME_CAPACITY
,&myStatus
);
3576 Locale min
= Locale::createFromName(minLocaleID
);
3578 if ( uprv_strlen(desiredLocale
.getCountry()) == 0 ||
3579 (uprv_strlen(desiredLocale
.getScript()) > 0 && uprv_strlen(min
.getScript()) == 0) ) {
3580 char maxLocaleID
[ULOC_FULLNAME_CAPACITY
] = { 0 };
3581 myStatus
= U_ZERO_ERROR
;
3582 uloc_addLikelySubtags(desiredLocale
.getName(),maxLocaleID
,ULOC_FULLNAME_CAPACITY
,&myStatus
);
3583 Locale max
= Locale::createFromName(maxLocaleID
);
3584 useLocale
= Locale(max
.getLanguage(),max
.getCountry());
3586 useLocale
= Locale(desiredLocale
);
3589 /* The code here is somewhat of a hack, since week data and weekend data aren't really tied to
3590 a specific calendar, they aren't truly locale data. But this is the only place where valid and
3591 actual locale can be set, so we take a shot at it here by loading a representative resource
3592 from the calendar data. The code used to use the dateTimeElements resource to get first day
3593 of week data, but this was moved to supplemental data under ticket 7755. (JCE) */
3595 CalendarData
calData(useLocale
,type
,status
);
3596 UResourceBundle
*monthNames
= calData
.getByKey(gMonthNames
,status
);
3597 if (U_SUCCESS(status
)) {
3598 U_LOCALE_BASED(locBased
,*this);
3599 locBased
.setLocaleIDs(ures_getLocaleByType(monthNames
, ULOC_VALID_LOCALE
, &status
),
3600 ures_getLocaleByType(monthNames
, ULOC_ACTUAL_LOCALE
, &status
));
3602 status
= U_USING_FALLBACK_WARNING
;
3607 // Read week data values from supplementalData week data
3608 UResourceBundle
*rb
= ures_openDirect(NULL
, "supplementalData", &status
);
3609 ures_getByKey(rb
, "weekData", rb
, &status
);
3610 UResourceBundle
*weekData
= ures_getByKey(rb
, useLocale
.getCountry(), NULL
, &status
);
3611 if (status
== U_MISSING_RESOURCE_ERROR
&& rb
!= NULL
) {
3612 status
= U_ZERO_ERROR
;
3613 weekData
= ures_getByKey(rb
, "001", NULL
, &status
);
3616 if (U_FAILURE(status
)) {
3617 #if defined (U_DEBUG_CALDATA)
3618 fprintf(stderr
, " Failure loading weekData from supplemental = %s\n", u_errorName(status
));
3620 status
= U_USING_FALLBACK_WARNING
;
3623 const int32_t *weekDataArr
= ures_getIntVector(weekData
,&arrLen
,&status
);
3624 if( U_SUCCESS(status
) && arrLen
== 6
3625 && 1 <= weekDataArr
[0] && weekDataArr
[0] <= 7
3626 && 1 <= weekDataArr
[1] && weekDataArr
[1] <= 7
3627 && 1 <= weekDataArr
[2] && weekDataArr
[2] <= 7
3628 && 1 <= weekDataArr
[4] && weekDataArr
[4] <= 7) {
3629 fFirstDayOfWeek
= (UCalendarDaysOfWeek
)weekDataArr
[0];
3630 fMinimalDaysInFirstWeek
= (uint8_t)weekDataArr
[1];
3631 fWeekendOnset
= (UCalendarDaysOfWeek
)weekDataArr
[2];
3632 fWeekendOnsetMillis
= weekDataArr
[3];
3633 fWeekendCease
= (UCalendarDaysOfWeek
)weekDataArr
[4];
3634 fWeekendCeaseMillis
= weekDataArr
[5];
3636 status
= U_INVALID_FORMAT_ERROR
;
3639 ures_close(weekData
);
3644 * Recompute the time and update the status fields isTimeSet
3645 * and areFieldsSet. Callers should check isTimeSet and only
3646 * call this method if isTimeSet is false.
3649 Calendar::updateTime(UErrorCode
& status
)
3651 computeTime(status
);
3652 if(U_FAILURE(status
))
3655 // If we are lenient, we need to recompute the fields to normalize
3656 // the values. Also, if we haven't set all the fields yet (i.e.,
3657 // in a newly-created object), we need to fill in the fields. [LIU]
3658 if (isLenient() || ! fAreAllFieldsSet
)
3659 fAreFieldsSet
= FALSE
;
3662 fAreFieldsVirtuallySet
= FALSE
;
3666 Calendar::getLocale(ULocDataLocaleType type
, UErrorCode
& status
) const {
3667 U_LOCALE_BASED(locBased
, *this);
3668 return locBased
.getLocale(type
, status
);
3672 Calendar::getLocaleID(ULocDataLocaleType type
, UErrorCode
& status
) const {
3673 U_LOCALE_BASED(locBased
, *this);
3674 return locBased
.getLocaleID(type
, status
);
3678 Calendar::recalculateStamp() {
3680 int32_t currentValue
;
3685 for (j
= 0; j
< UCAL_FIELD_COUNT
; j
++) {
3686 currentValue
= STAMP_MAX
;
3688 for (i
= 0; i
< UCAL_FIELD_COUNT
; i
++) {
3689 if (fStamp
[i
] > fNextStamp
&& fStamp
[i
] < currentValue
) {
3690 currentValue
= fStamp
[i
];
3696 fStamp
[index
] = ++fNextStamp
;
3704 // Deprecated function. This doesn't need to be inline.
3706 Calendar::internalSet(EDateFields field
, int32_t value
)
3708 internalSet((UCalendarDateFields
) field
, value
);
3712 Calendar::getBasicTimeZone(void) const {
3713 if (dynamic_cast<const OlsonTimeZone
*>(fZone
) != NULL
3714 || dynamic_cast<const SimpleTimeZone
*>(fZone
) != NULL
3715 || dynamic_cast<const RuleBasedTimeZone
*>(fZone
) != NULL
3716 || dynamic_cast<const VTimeZone
*>(fZone
) != NULL
) {
3717 return (BasicTimeZone
*)fZone
;
3724 #endif /* #if !UCONFIG_NO_FORMATTING */