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