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