1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 * Copyright (C) 1997-2016, International Business Machines Corporation and *
6 * others. All Rights Reserved. *
7 *******************************************************************************
8 *
9 * File CALENDAR.CPP
10 *
11 * Modification History:
12 *
13 * Date Name Description
14 * 02/03/97 clhuang Creation.
15 * 04/22/97 aliu Cleaned up, fixed memory leak, made
16 * setWeekCountData() more robust.
17 * Moved platform code to TPlatformUtilities.
18 * 05/01/97 aliu Made equals(), before(), after() arguments const.
19 * 05/20/97 aliu Changed logic of when to compute fields and time
20 * to fix bugs.
21 * 08/12/97 aliu Added equivalentTo. Misc other fixes.
22 * 07/28/98 stephen Sync up with JDK 1.2
23 * 09/02/98 stephen Sync with JDK 1.2 8/31 build (getActualMin/Max)
24 * 03/17/99 stephen Changed adoptTimeZone() - now fAreFieldsSet is
25 * set to FALSE to force update of time.
26 *******************************************************************************
27 */
28
29 #include "utypeinfo.h" // for 'typeid' to work
30
31 #include "unicode/utypes.h"
32
33 #if !UCONFIG_NO_FORMATTING
34
35 #include "unicode/gregocal.h"
36 #include "unicode/basictz.h"
37 #include "unicode/simpletz.h"
38 #include "unicode/rbtz.h"
39 #include "unicode/vtzone.h"
40 #include "gregoimp.h"
41 #include "buddhcal.h"
42 #include "taiwncal.h"
43 #include "japancal.h"
44 #include "islamcal.h"
45 #include "hebrwcal.h"
46 #include "persncal.h"
47 #include "indiancal.h"
48 #include "chnsecal.h"
49 #include "coptccal.h"
50 #include "dangical.h"
51 #include "ethpccal.h"
52 #include "unicode/calendar.h"
53 #include "cpputils.h"
54 #include "servloc.h"
55 #include "ucln_in.h"
56 #include "cstring.h"
57 #include "locbased.h"
58 #include "uresimp.h"
59 #include "ustrenum.h"
60 #include "uassert.h"
61 #include "olsontz.h"
62 #include "sharedcalendar.h"
63 #include "unifiedcache.h"
64 #include "ulocimp.h"
65
66 #if !UCONFIG_NO_SERVICE
67 static icu::ICULocaleService* gService = NULL;
68 static icu::UInitOnce gServiceInitOnce = U_INITONCE_INITIALIZER;
69 #endif
70
71 // INTERNAL - for cleanup
72
73 U_CDECL_BEGIN
calendar_cleanup(void)74 static UBool calendar_cleanup(void) {
75 #if !UCONFIG_NO_SERVICE
76 if (gService) {
77 delete gService;
78 gService = NULL;
79 }
80 gServiceInitOnce.reset();
81 #endif
82 return TRUE;
83 }
84 U_CDECL_END
85
86 // ------------------------------------------
87 //
88 // Registration
89 //
90 //-------------------------------------------
91 //#define U_DEBUG_CALSVC 1
92 //
93
94 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
95
96 /**
97 * fldName was removed as a duplicate implementation.
98 * use udbg_ services instead,
99 * which depend on include files and library from ../tools/toolutil, the following circular link:
100 * CPPFLAGS+=-I$(top_srcdir)/tools/toolutil
101 * LIBS+=$(LIBICUTOOLUTIL)
102 */
103 #include "udbgutil.h"
104 #include <stdio.h>
105
106 /**
107 * convert a UCalendarDateFields into a string - for debugging
108 * @param f field enum
109 * @return static string to the field name
110 * @internal
111 */
112
fldName(UCalendarDateFields f)113 const char* fldName(UCalendarDateFields f) {
114 return udbg_enumName(UDBG_UCalendarDateFields, (int32_t)f);
115 }
116
117 #if UCAL_DEBUG_DUMP
118 // from CalendarTest::calToStr - but doesn't modify contents.
ucal_dump(const Calendar & cal)119 void ucal_dump(const Calendar &cal) {
120 cal.dump();
121 }
122
dump() const123 void Calendar::dump() const {
124 int i;
125 fprintf(stderr, "@calendar=%s, timeset=%c, fieldset=%c, allfields=%c, virtualset=%c, t=%.2f",
126 getType(), fIsTimeSet?'y':'n', fAreFieldsSet?'y':'n', fAreAllFieldsSet?'y':'n',
127 fAreFieldsVirtuallySet?'y':'n',
128 fTime);
129
130 // can add more things here: DST, zone, etc.
131 fprintf(stderr, "\n");
132 for(i = 0;i<UCAL_FIELD_COUNT;i++) {
133 int n;
134 const char *f = fldName((UCalendarDateFields)i);
135 fprintf(stderr, " %25s: %-11ld", f, fFields[i]);
136 if(fStamp[i] == kUnset) {
137 fprintf(stderr, " (unset) ");
138 } else if(fStamp[i] == kInternallySet) {
139 fprintf(stderr, " (internally set) ");
140 //} else if(fStamp[i] == kInternalDefault) {
141 // fprintf(stderr, " (internal default) ");
142 } else {
143 fprintf(stderr, " %%%d ", fStamp[i]);
144 }
145 fprintf(stderr, "\n");
146
147 }
148 }
149
ucal_dump(UCalendar * cal)150 U_CFUNC void ucal_dump(UCalendar* cal) {
151 ucal_dump( *((Calendar*)cal) );
152 }
153 #endif
154
155 #endif
156
157 /* Max value for stamp allowable before recalculation */
158 #define STAMP_MAX 10000
159
160 static const char * const gCalTypes[] = {
161 "gregorian",
162 "japanese",
163 "buddhist",
164 "roc",
165 "persian",
166 "islamic-civil",
167 "islamic",
168 "hebrew",
169 "chinese",
170 "indian",
171 "coptic",
172 "ethiopic",
173 "ethiopic-amete-alem",
174 "iso8601",
175 "dangi",
176 "islamic-umalqura",
177 "islamic-tbla",
178 "islamic-rgsa",
179 NULL
180 };
181
182 // Must be in the order of gCalTypes above
183 typedef enum ECalType {
184 CALTYPE_UNKNOWN = -1,
185 CALTYPE_GREGORIAN = 0,
186 CALTYPE_JAPANESE,
187 CALTYPE_BUDDHIST,
188 CALTYPE_ROC,
189 CALTYPE_PERSIAN,
190 CALTYPE_ISLAMIC_CIVIL,
191 CALTYPE_ISLAMIC,
192 CALTYPE_HEBREW,
193 CALTYPE_CHINESE,
194 CALTYPE_INDIAN,
195 CALTYPE_COPTIC,
196 CALTYPE_ETHIOPIC,
197 CALTYPE_ETHIOPIC_AMETE_ALEM,
198 CALTYPE_ISO8601,
199 CALTYPE_DANGI,
200 CALTYPE_ISLAMIC_UMALQURA,
201 CALTYPE_ISLAMIC_TBLA,
202 CALTYPE_ISLAMIC_RGSA
203 } ECalType;
204
205 U_NAMESPACE_BEGIN
206
~SharedCalendar()207 SharedCalendar::~SharedCalendar() {
208 delete ptr;
209 }
210
211 template<> U_I18N_API
createObject(const void *,UErrorCode & status) const212 const SharedCalendar *LocaleCacheKey<SharedCalendar>::createObject(
213 const void * /*unusedCreationContext*/, UErrorCode &status) const {
214 Calendar *calendar = Calendar::makeInstance(fLoc, status);
215 if (U_FAILURE(status)) {
216 return NULL;
217 }
218 SharedCalendar *shared = new SharedCalendar(calendar);
219 if (shared == NULL) {
220 delete calendar;
221 status = U_MEMORY_ALLOCATION_ERROR;
222 return NULL;
223 }
224 shared->addRef();
225 return shared;
226 }
227
getCalendarType(const char * s)228 static ECalType getCalendarType(const char *s) {
229 for (int i = 0; gCalTypes[i] != NULL; i++) {
230 if (uprv_stricmp(s, gCalTypes[i]) == 0) {
231 return (ECalType)i;
232 }
233 }
234 return CALTYPE_UNKNOWN;
235 }
236
isStandardSupportedKeyword(const char * keyword,UErrorCode & status)237 static UBool isStandardSupportedKeyword(const char *keyword, UErrorCode& status) {
238 if(U_FAILURE(status)) {
239 return FALSE;
240 }
241 ECalType calType = getCalendarType(keyword);
242 return (calType != CALTYPE_UNKNOWN);
243 }
244
getCalendarKeyword(const UnicodeString & id,char * targetBuffer,int32_t targetBufferSize)245 static void getCalendarKeyword(const UnicodeString &id, char *targetBuffer, int32_t targetBufferSize) {
246 UnicodeString calendarKeyword = UNICODE_STRING_SIMPLE("calendar=");
247 int32_t calKeyLen = calendarKeyword.length();
248 int32_t keyLen = 0;
249
250 int32_t keywordIdx = id.indexOf((UChar)0x003D); /* '=' */
251 if (id[0] == 0x40/*'@'*/
252 && id.compareBetween(1, keywordIdx+1, calendarKeyword, 0, calKeyLen) == 0)
253 {
254 keyLen = id.extract(keywordIdx+1, id.length(), targetBuffer, targetBufferSize, US_INV);
255 }
256 targetBuffer[keyLen] = 0;
257 }
258
getCalendarTypeForLocale(const char * locid)259 static ECalType getCalendarTypeForLocale(const char *locid) {
260 UErrorCode status = U_ZERO_ERROR;
261 ECalType calType = CALTYPE_UNKNOWN;
262
263 //TODO: ULOC_FULL_NAME is out of date and too small..
264 char canonicalName[256];
265
266 // canonicalize, so grandfathered variant will be transformed to keywords
267 // e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese
268 int32_t canonicalLen = uloc_canonicalize(locid, canonicalName, sizeof(canonicalName) - 1, &status);
269 if (U_FAILURE(status)) {
270 return CALTYPE_GREGORIAN;
271 }
272 canonicalName[canonicalLen] = 0; // terminate
273
274 char calTypeBuf[32];
275 int32_t calTypeBufLen;
276
277 calTypeBufLen = uloc_getKeywordValue(canonicalName, "calendar", calTypeBuf, sizeof(calTypeBuf) - 1, &status);
278 if (U_SUCCESS(status)) {
279 calTypeBuf[calTypeBufLen] = 0;
280 calType = getCalendarType(calTypeBuf);
281 if (calType != CALTYPE_UNKNOWN) {
282 return calType;
283 }
284 }
285 status = U_ZERO_ERROR;
286
287 // when calendar keyword is not available or not supported, read supplementalData
288 // to get the default calendar type for the locale's region
289 char region[ULOC_COUNTRY_CAPACITY];
290 (void)ulocimp_getRegionForSupplementalData(canonicalName, TRUE, region, sizeof(region), &status);
291 if (U_FAILURE(status)) {
292 return CALTYPE_GREGORIAN;
293 }
294
295 // Read preferred calendar values from supplementalData calendarPreference
296 UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", &status);
297 ures_getByKey(rb, "calendarPreferenceData", rb, &status);
298 UResourceBundle *order = ures_getByKey(rb, region, NULL, &status);
299 if (status == U_MISSING_RESOURCE_ERROR && rb != NULL) {
300 status = U_ZERO_ERROR;
301 order = ures_getByKey(rb, "001", NULL, &status);
302 }
303
304 calTypeBuf[0] = 0;
305 if (U_SUCCESS(status) && order != NULL) {
306 // the first calender type is the default for the region
307 int32_t len = 0;
308 const UChar *uCalType = ures_getStringByIndex(order, 0, &len, &status);
309 if (len < (int32_t)sizeof(calTypeBuf)) {
310 u_UCharsToChars(uCalType, calTypeBuf, len);
311 *(calTypeBuf + len) = 0; // terminate;
312 calType = getCalendarType(calTypeBuf);
313 }
314 }
315
316 ures_close(order);
317 ures_close(rb);
318
319 if (calType == CALTYPE_UNKNOWN) {
320 // final fallback
321 calType = CALTYPE_GREGORIAN;
322 }
323 return calType;
324 }
325
createStandardCalendar(ECalType calType,const Locale & loc,UErrorCode & status)326 static Calendar *createStandardCalendar(ECalType calType, const Locale &loc, UErrorCode& status) {
327 Calendar *cal = NULL;
328
329 switch (calType) {
330 case CALTYPE_GREGORIAN:
331 cal = new GregorianCalendar(loc, status);
332 break;
333 case CALTYPE_JAPANESE:
334 cal = new JapaneseCalendar(loc, status);
335 break;
336 case CALTYPE_BUDDHIST:
337 cal = new BuddhistCalendar(loc, status);
338 break;
339 case CALTYPE_ROC:
340 cal = new TaiwanCalendar(loc, status);
341 break;
342 case CALTYPE_PERSIAN:
343 cal = new PersianCalendar(loc, status);
344 break;
345 case CALTYPE_ISLAMIC_TBLA:
346 cal = new IslamicCalendar(loc, status, IslamicCalendar::TBLA);
347 break;
348 case CALTYPE_ISLAMIC_CIVIL:
349 cal = new IslamicCalendar(loc, status, IslamicCalendar::CIVIL);
350 break;
351 case CALTYPE_ISLAMIC_RGSA:
352 // default any region specific not handled individually to islamic
353 case CALTYPE_ISLAMIC:
354 cal = new IslamicCalendar(loc, status, IslamicCalendar::ASTRONOMICAL);
355 break;
356 case CALTYPE_ISLAMIC_UMALQURA:
357 cal = new IslamicCalendar(loc, status, IslamicCalendar::UMALQURA);
358 break;
359 case CALTYPE_HEBREW:
360 cal = new HebrewCalendar(loc, status);
361 break;
362 case CALTYPE_CHINESE:
363 cal = new ChineseCalendar(loc, status);
364 break;
365 case CALTYPE_INDIAN:
366 cal = new IndianCalendar(loc, status);
367 break;
368 case CALTYPE_COPTIC:
369 cal = new CopticCalendar(loc, status);
370 break;
371 case CALTYPE_ETHIOPIC:
372 cal = new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_MIHRET_ERA);
373 break;
374 case CALTYPE_ETHIOPIC_AMETE_ALEM:
375 cal = new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_ALEM_ERA);
376 break;
377 case CALTYPE_ISO8601:
378 cal = new GregorianCalendar(loc, status);
379 cal->setFirstDayOfWeek(UCAL_MONDAY);
380 cal->setMinimalDaysInFirstWeek(4);
381 break;
382 case CALTYPE_DANGI:
383 cal = new DangiCalendar(loc, status);
384 break;
385 default:
386 status = U_UNSUPPORTED_ERROR;
387 }
388 return cal;
389 }
390
391
392 #if !UCONFIG_NO_SERVICE
393
394 // -------------------------------------
395
396 /**
397 * a Calendar Factory which creates the "basic" calendar types, that is, those
398 * shipped with ICU.
399 */
400 class BasicCalendarFactory : public LocaleKeyFactory {
401 public:
402 /**
403 * @param calendarType static const string (caller owns storage - will be aliased) to calendar type
404 */
BasicCalendarFactory()405 BasicCalendarFactory()
406 : LocaleKeyFactory(LocaleKeyFactory::INVISIBLE) { }
407
408 virtual ~BasicCalendarFactory();
409
410 protected:
411 //virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const {
412 // if(U_FAILURE(status)) {
413 // return FALSE;
414 // }
415 // char keyword[ULOC_FULLNAME_CAPACITY];
416 // getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword));
417 // return isStandardSupportedKeyword(keyword, status);
418 //}
419
updateVisibleIDs(Hashtable & result,UErrorCode & status) const420 virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const
421 {
422 if (U_SUCCESS(status)) {
423 for(int32_t i=0;gCalTypes[i] != NULL;i++) {
424 UnicodeString id((UChar)0x40); /* '@' a variant character */
425 id.append(UNICODE_STRING_SIMPLE("calendar="));
426 id.append(UnicodeString(gCalTypes[i], -1, US_INV));
427 result.put(id, (void*)this, status);
428 }
429 }
430 }
431
create(const ICUServiceKey & key,const ICUService *,UErrorCode & status) const432 virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const {
433 #ifdef U_DEBUG_CALSVC
434 if(dynamic_cast<const LocaleKey*>(&key) == NULL) {
435 fprintf(stderr, "::create - not a LocaleKey!\n");
436 }
437 #endif
438 const LocaleKey& lkey = (LocaleKey&)key;
439 Locale curLoc; // current locale
440 Locale canLoc; // Canonical locale
441
442 lkey.currentLocale(curLoc);
443 lkey.canonicalLocale(canLoc);
444
445 char keyword[ULOC_FULLNAME_CAPACITY];
446 UnicodeString str;
447
448 key.currentID(str);
449 getCalendarKeyword(str, keyword, (int32_t) sizeof(keyword));
450
451 #ifdef U_DEBUG_CALSVC
452 fprintf(stderr, "BasicCalendarFactory::create() - cur %s, can %s\n", (const char*)curLoc.getName(), (const char*)canLoc.getName());
453 #endif
454
455 if(!isStandardSupportedKeyword(keyword,status)) { // Do we handle this type?
456 #ifdef U_DEBUG_CALSVC
457
458 fprintf(stderr, "BasicCalendarFactory - not handling %s.[%s]\n", (const char*) curLoc.getName(), tmp );
459 #endif
460 return NULL;
461 }
462
463 return createStandardCalendar(getCalendarType(keyword), canLoc, status);
464 }
465 };
466
~BasicCalendarFactory()467 BasicCalendarFactory::~BasicCalendarFactory() {}
468
469 /**
470 * A factory which looks up the DefaultCalendar resource to determine which class of calendar to use
471 */
472
473 class DefaultCalendarFactory : public ICUResourceBundleFactory {
474 public:
DefaultCalendarFactory()475 DefaultCalendarFactory() : ICUResourceBundleFactory() { }
476 virtual ~DefaultCalendarFactory();
477 protected:
create(const ICUServiceKey & key,const ICUService *,UErrorCode & status) const478 virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const {
479
480 LocaleKey &lkey = (LocaleKey&)key;
481 Locale loc;
482 lkey.currentLocale(loc);
483
484 UnicodeString *ret = new UnicodeString();
485 if (ret == NULL) {
486 status = U_MEMORY_ALLOCATION_ERROR;
487 } else {
488 ret->append((UChar)0x40); // '@' is a variant character
489 ret->append(UNICODE_STRING("calendar=", 9));
490 ret->append(UnicodeString(gCalTypes[getCalendarTypeForLocale(loc.getName())], -1, US_INV));
491 }
492 return ret;
493 }
494 };
495
~DefaultCalendarFactory()496 DefaultCalendarFactory::~DefaultCalendarFactory() {}
497
498 // -------------------------------------
499 class CalendarService : public ICULocaleService {
500 public:
CalendarService()501 CalendarService()
502 : ICULocaleService(UNICODE_STRING_SIMPLE("Calendar"))
503 {
504 UErrorCode status = U_ZERO_ERROR;
505 registerFactory(new DefaultCalendarFactory(), status);
506 }
507
508 virtual ~CalendarService();
509
cloneInstance(UObject * instance) const510 virtual UObject* cloneInstance(UObject* instance) const {
511 UnicodeString *s = dynamic_cast<UnicodeString *>(instance);
512 if(s != NULL) {
513 return s->clone();
514 } else {
515 #ifdef U_DEBUG_CALSVC_F
516 UErrorCode status2 = U_ZERO_ERROR;
517 fprintf(stderr, "Cloning a %s calendar with tz=%ld\n", ((Calendar*)instance)->getType(), ((Calendar*)instance)->get(UCAL_ZONE_OFFSET, status2));
518 #endif
519 return ((Calendar*)instance)->clone();
520 }
521 }
522
handleDefault(const ICUServiceKey & key,UnicodeString *,UErrorCode & status) const523 virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /*actualID*/, UErrorCode& status) const {
524 LocaleKey& lkey = (LocaleKey&)key;
525 //int32_t kind = lkey.kind();
526
527 Locale loc;
528 lkey.canonicalLocale(loc);
529
530 #ifdef U_DEBUG_CALSVC
531 Locale loc2;
532 lkey.currentLocale(loc2);
533 fprintf(stderr, "CalSvc:handleDefault for currentLoc %s, canloc %s\n", (const char*)loc.getName(), (const char*)loc2.getName());
534 #endif
535 Calendar *nc = new GregorianCalendar(loc, status);
536
537 #ifdef U_DEBUG_CALSVC
538 UErrorCode status2 = U_ZERO_ERROR;
539 fprintf(stderr, "New default calendar has tz=%d\n", ((Calendar*)nc)->get(UCAL_ZONE_OFFSET, status2));
540 #endif
541 return nc;
542 }
543
isDefault() const544 virtual UBool isDefault() const {
545 return countFactories() == 1;
546 }
547 };
548
~CalendarService()549 CalendarService::~CalendarService() {}
550
551 // -------------------------------------
552
553 static inline UBool
isCalendarServiceUsed()554 isCalendarServiceUsed() {
555 return !gServiceInitOnce.isReset();
556 }
557
558 // -------------------------------------
559
560 static void U_CALLCONV
initCalendarService(UErrorCode & status)561 initCalendarService(UErrorCode &status)
562 {
563 #ifdef U_DEBUG_CALSVC
564 fprintf(stderr, "Spinning up Calendar Service\n");
565 #endif
566 ucln_i18n_registerCleanup(UCLN_I18N_CALENDAR, calendar_cleanup);
567 gService = new CalendarService();
568 if (gService == NULL) {
569 status = U_MEMORY_ALLOCATION_ERROR;
570 return;
571 }
572 #ifdef U_DEBUG_CALSVC
573 fprintf(stderr, "Registering classes..\n");
574 #endif
575
576 // Register all basic instances.
577 gService->registerFactory(new BasicCalendarFactory(),status);
578
579 #ifdef U_DEBUG_CALSVC
580 fprintf(stderr, "Done..\n");
581 #endif
582
583 if(U_FAILURE(status)) {
584 #ifdef U_DEBUG_CALSVC
585 fprintf(stderr, "err (%s) registering classes, deleting service.....\n", u_errorName(status));
586 #endif
587 delete gService;
588 gService = NULL;
589 }
590 }
591
592 static ICULocaleService*
getCalendarService(UErrorCode & status)593 getCalendarService(UErrorCode &status)
594 {
595 umtx_initOnce(gServiceInitOnce, &initCalendarService, status);
596 return gService;
597 }
598
registerFactory(ICUServiceFactory * toAdopt,UErrorCode & status)599 URegistryKey Calendar::registerFactory(ICUServiceFactory* toAdopt, UErrorCode& status)
600 {
601 return getCalendarService(status)->registerFactory(toAdopt, status);
602 }
603
unregister(URegistryKey key,UErrorCode & status)604 UBool Calendar::unregister(URegistryKey key, UErrorCode& status) {
605 return getCalendarService(status)->unregister(key, status);
606 }
607 #endif /* UCONFIG_NO_SERVICE */
608
609 // -------------------------------------
610
611 static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = {
612 // Minimum Greatest min Least max Greatest max
613 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // ERA
614 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR
615 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // MONTH
616 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_YEAR
617 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_MONTH
618 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_MONTH
619 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_YEAR
620 { 1, 1, 7, 7 }, // DAY_OF_WEEK
621 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_WEEK_IN_MONTH
622 { 0, 0, 1, 1 }, // AM_PM
623 { 0, 0, 11, 11 }, // HOUR
624 { 0, 0, 23, 23 }, // HOUR_OF_DAY
625 { 0, 0, 59, 59 }, // MINUTE
626 { 0, 0, 59, 59 }, // SECOND
627 { 0, 0, 999, 999 }, // MILLISECOND
628 {-12*kOneHour, -12*kOneHour, 12*kOneHour, 15*kOneHour }, // ZONE_OFFSET
629 { 0, 0, 1*kOneHour, 1*kOneHour }, // DST_OFFSET
630 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR_WOY
631 { 1, 1, 7, 7 }, // DOW_LOCAL
632 {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // EXTENDED_YEAR
633 { -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY
634 { 0, 0, 24*kOneHour-1, 24*kOneHour-1 }, // MILLISECONDS_IN_DAY
635 { 0, 0, 1, 1 }, // IS_LEAP_MONTH
636 };
637
638 // Resource bundle tags read by this class
639 static const char gCalendar[] = "calendar";
640 static const char gMonthNames[] = "monthNames";
641 static const char gGregorian[] = "gregorian";
642
643 // Data flow in Calendar
644 // ---------------------
645
646 // The current time is represented in two ways by Calendar: as UTC
647 // milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local
648 // fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the
649 // millis from the fields, and vice versa. The data needed to do this
650 // conversion is encapsulated by a TimeZone object owned by the Calendar.
651 // The data provided by the TimeZone object may also be overridden if the
652 // user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class
653 // keeps track of what information was most recently set by the caller, and
654 // uses that to compute any other information as needed.
655
656 // If the user sets the fields using set(), the data flow is as follows.
657 // This is implemented by the Calendar subclass's computeTime() method.
658 // During this process, certain fields may be ignored. The disambiguation
659 // algorithm for resolving which fields to pay attention to is described
660 // above.
661
662 // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
663 // |
664 // | Using Calendar-specific algorithm
665 // V
666 // local standard millis
667 // |
668 // | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET
669 // V
670 // UTC millis (in time data member)
671
672 // If the user sets the UTC millis using setTime(), the data flow is as
673 // follows. This is implemented by the Calendar subclass's computeFields()
674 // method.
675
676 // UTC millis (in time data member)
677 // |
678 // | Using TimeZone getOffset()
679 // V
680 // local standard millis
681 // |
682 // | Using Calendar-specific algorithm
683 // V
684 // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
685
686 // In general, a round trip from fields, through local and UTC millis, and
687 // back out to fields is made when necessary. This is implemented by the
688 // complete() method. Resolving a partial set of fields into a UTC millis
689 // value allows all remaining fields to be generated from that value. If
690 // the Calendar is lenient, the fields are also renormalized to standard
691 // ranges when they are regenerated.
692
693 // -------------------------------------
694
Calendar(UErrorCode & success)695 Calendar::Calendar(UErrorCode& success)
696 : UObject(),
697 fIsTimeSet(FALSE),
698 fAreFieldsSet(FALSE),
699 fAreAllFieldsSet(FALSE),
700 fAreFieldsVirtuallySet(FALSE),
701 fNextStamp((int32_t)kMinimumUserStamp),
702 fTime(0),
703 fLenient(TRUE),
704 fZone(NULL),
705 fRepeatedWallTime(UCAL_WALLTIME_LAST),
706 fSkippedWallTime(UCAL_WALLTIME_LAST)
707 {
708 clear();
709 if (U_FAILURE(success)) {
710 return;
711 }
712 fZone = TimeZone::createDefault();
713 if (fZone == NULL) {
714 success = U_MEMORY_ALLOCATION_ERROR;
715 }
716 setWeekData(Locale::getDefault(), NULL, success);
717 }
718
719 // -------------------------------------
720
Calendar(TimeZone * zone,const Locale & aLocale,UErrorCode & success)721 Calendar::Calendar(TimeZone* zone, const Locale& aLocale, UErrorCode& success)
722 : UObject(),
723 fIsTimeSet(FALSE),
724 fAreFieldsSet(FALSE),
725 fAreAllFieldsSet(FALSE),
726 fAreFieldsVirtuallySet(FALSE),
727 fNextStamp((int32_t)kMinimumUserStamp),
728 fTime(0),
729 fLenient(TRUE),
730 fZone(NULL),
731 fRepeatedWallTime(UCAL_WALLTIME_LAST),
732 fSkippedWallTime(UCAL_WALLTIME_LAST)
733 {
734 if (U_FAILURE(success)) {
735 return;
736 }
737 if(zone == 0) {
738 #if defined (U_DEBUG_CAL)
739 fprintf(stderr, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n",
740 __FILE__, __LINE__);
741 #endif
742 success = U_ILLEGAL_ARGUMENT_ERROR;
743 return;
744 }
745
746 clear();
747 fZone = zone;
748 setWeekData(aLocale, NULL, success);
749 }
750
751 // -------------------------------------
752
Calendar(const TimeZone & zone,const Locale & aLocale,UErrorCode & success)753 Calendar::Calendar(const TimeZone& zone, const Locale& aLocale, UErrorCode& success)
754 : UObject(),
755 fIsTimeSet(FALSE),
756 fAreFieldsSet(FALSE),
757 fAreAllFieldsSet(FALSE),
758 fAreFieldsVirtuallySet(FALSE),
759 fNextStamp((int32_t)kMinimumUserStamp),
760 fTime(0),
761 fLenient(TRUE),
762 fZone(NULL),
763 fRepeatedWallTime(UCAL_WALLTIME_LAST),
764 fSkippedWallTime(UCAL_WALLTIME_LAST)
765 {
766 if (U_FAILURE(success)) {
767 return;
768 }
769 clear();
770 fZone = zone.clone();
771 if (fZone == NULL) {
772 success = U_MEMORY_ALLOCATION_ERROR;
773 }
774 setWeekData(aLocale, NULL, success);
775 }
776
777 // -------------------------------------
778
~Calendar()779 Calendar::~Calendar()
780 {
781 delete fZone;
782 }
783
784 // -------------------------------------
785
Calendar(const Calendar & source)786 Calendar::Calendar(const Calendar &source)
787 : UObject(source)
788 {
789 fZone = NULL;
790 *this = source;
791 }
792
793 // -------------------------------------
794
795 Calendar &
operator =(const Calendar & right)796 Calendar::operator=(const Calendar &right)
797 {
798 if (this != &right) {
799 uprv_arrayCopy(right.fFields, fFields, UCAL_FIELD_COUNT);
800 uprv_arrayCopy(right.fIsSet, fIsSet, UCAL_FIELD_COUNT);
801 uprv_arrayCopy(right.fStamp, fStamp, UCAL_FIELD_COUNT);
802 fTime = right.fTime;
803 fIsTimeSet = right.fIsTimeSet;
804 fAreAllFieldsSet = right.fAreAllFieldsSet;
805 fAreFieldsSet = right.fAreFieldsSet;
806 fAreFieldsVirtuallySet = right.fAreFieldsVirtuallySet;
807 fLenient = right.fLenient;
808 fRepeatedWallTime = right.fRepeatedWallTime;
809 fSkippedWallTime = right.fSkippedWallTime;
810 delete fZone;
811 fZone = NULL;
812 if (right.fZone != NULL) {
813 fZone = right.fZone->clone();
814 }
815 fFirstDayOfWeek = right.fFirstDayOfWeek;
816 fMinimalDaysInFirstWeek = right.fMinimalDaysInFirstWeek;
817 fWeekendOnset = right.fWeekendOnset;
818 fWeekendOnsetMillis = right.fWeekendOnsetMillis;
819 fWeekendCease = right.fWeekendCease;
820 fWeekendCeaseMillis = right.fWeekendCeaseMillis;
821 fNextStamp = right.fNextStamp;
822 uprv_strcpy(validLocale, right.validLocale);
823 uprv_strcpy(actualLocale, right.actualLocale);
824 }
825
826 return *this;
827 }
828
829 // -------------------------------------
830
831 Calendar* U_EXPORT2
createInstance(UErrorCode & success)832 Calendar::createInstance(UErrorCode& success)
833 {
834 return createInstance(TimeZone::createDefault(), Locale::getDefault(), success);
835 }
836
837 // -------------------------------------
838
839 Calendar* U_EXPORT2
createInstance(const TimeZone & zone,UErrorCode & success)840 Calendar::createInstance(const TimeZone& zone, UErrorCode& success)
841 {
842 return createInstance(zone, Locale::getDefault(), success);
843 }
844
845 // -------------------------------------
846
847 Calendar* U_EXPORT2
createInstance(const Locale & aLocale,UErrorCode & success)848 Calendar::createInstance(const Locale& aLocale, UErrorCode& success)
849 {
850 return createInstance(TimeZone::createDefault(), aLocale, success);
851 }
852
853 // ------------------------------------- Adopting
854
855 // Note: this is the bottleneck that actually calls the service routines.
856
857 Calendar * U_EXPORT2
makeInstance(const Locale & aLocale,UErrorCode & success)858 Calendar::makeInstance(const Locale& aLocale, UErrorCode& success) {
859 if (U_FAILURE(success)) {
860 return NULL;
861 }
862
863 Locale actualLoc;
864 UObject* u = NULL;
865
866 #if !UCONFIG_NO_SERVICE
867 if (isCalendarServiceUsed()) {
868 u = getCalendarService(success)->get(aLocale, LocaleKey::KIND_ANY, &actualLoc, success);
869 }
870 else
871 #endif
872 {
873 u = createStandardCalendar(getCalendarTypeForLocale(aLocale.getName()), aLocale, success);
874 }
875 Calendar* c = NULL;
876
877 if(U_FAILURE(success) || !u) {
878 if(U_SUCCESS(success)) { // Propagate some kind of err
879 success = U_INTERNAL_PROGRAM_ERROR;
880 }
881 return NULL;
882 }
883
884 #if !UCONFIG_NO_SERVICE
885 const UnicodeString* str = dynamic_cast<const UnicodeString*>(u);
886 if(str != NULL) {
887 // It's a unicode string telling us what type of calendar to load ("gregorian", etc)
888 // Create a Locale over this string
889 Locale l("");
890 LocaleUtility::initLocaleFromName(*str, l);
891
892 #ifdef U_DEBUG_CALSVC
893 fprintf(stderr, "Calendar::createInstance(%s), looking up [%s]\n", aLocale.getName(), l.getName());
894 #endif
895
896 Locale actualLoc2;
897 delete u;
898 u = NULL;
899
900 // Don't overwrite actualLoc, since the actual loc from this call
901 // may be something like "@calendar=gregorian" -- TODO investigate
902 // further...
903 c = (Calendar*)getCalendarService(success)->get(l, LocaleKey::KIND_ANY, &actualLoc2, success);
904
905 if(U_FAILURE(success) || !c) {
906 if(U_SUCCESS(success)) {
907 success = U_INTERNAL_PROGRAM_ERROR; // Propagate some err
908 }
909 return NULL;
910 }
911
912 str = dynamic_cast<const UnicodeString*>(c);
913 if(str != NULL) {
914 // recursed! Second lookup returned a UnicodeString.
915 // Perhaps DefaultCalendar{} was set to another locale.
916 #ifdef U_DEBUG_CALSVC
917 char tmp[200];
918 // Extract a char* out of it..
919 int32_t len = str->length();
920 int32_t actLen = sizeof(tmp)-1;
921 if(len > actLen) {
922 len = actLen;
923 }
924 str->extract(0,len,tmp);
925 tmp[len]=0;
926
927 fprintf(stderr, "err - recursed, 2nd lookup was unistring %s\n", tmp);
928 #endif
929 success = U_MISSING_RESOURCE_ERROR; // requested a calendar type which could NOT be found.
930 delete c;
931 return NULL;
932 }
933 #ifdef U_DEBUG_CALSVC
934 fprintf(stderr, "%p: setting week count data to locale %s, actual locale %s\n", c, (const char*)aLocale.getName(), (const char *)actualLoc.getName());
935 #endif
936 c->setWeekData(aLocale, c->getType(), success); // set the correct locale (this was an indirected calendar)
937
938 char keyword[ULOC_FULLNAME_CAPACITY];
939 UErrorCode tmpStatus = U_ZERO_ERROR;
940 l.getKeywordValue("calendar", keyword, ULOC_FULLNAME_CAPACITY, tmpStatus);
941 if (U_SUCCESS(tmpStatus) && uprv_strcmp(keyword, "iso8601") == 0) {
942 c->setFirstDayOfWeek(UCAL_MONDAY);
943 c->setMinimalDaysInFirstWeek(4);
944 }
945 }
946 else
947 #endif /* UCONFIG_NO_SERVICE */
948 {
949 // a calendar was returned - we assume the factory did the right thing.
950 c = (Calendar*)u;
951 }
952
953 return c;
954 }
955
956 Calendar* U_EXPORT2
createInstance(TimeZone * zone,const Locale & aLocale,UErrorCode & success)957 Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success)
958 {
959 LocalPointer<TimeZone> zonePtr(zone);
960 const SharedCalendar *shared = NULL;
961 UnifiedCache::getByLocale(aLocale, shared, success);
962 if (U_FAILURE(success)) {
963 return NULL;
964 }
965 Calendar *c = (*shared)->clone();
966 shared->removeRef();
967 if (c == NULL) {
968 success = U_MEMORY_ALLOCATION_ERROR;
969 return NULL;
970 }
971
972 // Now, reset calendar to default state:
973 c->adoptTimeZone(zonePtr.orphan()); // Set the correct time zone
974 c->setTimeInMillis(getNow(), success); // let the new calendar have the current time.
975
976 return c;
977 }
978
979 // -------------------------------------
980
981 Calendar* U_EXPORT2
createInstance(const TimeZone & zone,const Locale & aLocale,UErrorCode & success)982 Calendar::createInstance(const TimeZone& zone, const Locale& aLocale, UErrorCode& success)
983 {
984 Calendar* c = createInstance(aLocale, success);
985 if(U_SUCCESS(success) && c) {
986 c->setTimeZone(zone);
987 }
988 return c;
989 }
990
991 // -------------------------------------
992
993 void U_EXPORT2
getCalendarTypeFromLocale(const Locale & aLocale,char * typeBuffer,int32_t typeBufferSize,UErrorCode & success)994 Calendar::getCalendarTypeFromLocale(
995 const Locale &aLocale,
996 char *typeBuffer,
997 int32_t typeBufferSize,
998 UErrorCode &success) {
999 const SharedCalendar *shared = NULL;
1000 UnifiedCache::getByLocale(aLocale, shared, success);
1001 if (U_FAILURE(success)) {
1002 return;
1003 }
1004 uprv_strncpy(typeBuffer, (*shared)->getType(), typeBufferSize);
1005 shared->removeRef();
1006 if (typeBuffer[typeBufferSize - 1]) {
1007 success = U_BUFFER_OVERFLOW_ERROR;
1008 }
1009 }
1010
1011 UBool
operator ==(const Calendar & that) const1012 Calendar::operator==(const Calendar& that) const
1013 {
1014 UErrorCode status = U_ZERO_ERROR;
1015 return isEquivalentTo(that) &&
1016 getTimeInMillis(status) == that.getTimeInMillis(status) &&
1017 U_SUCCESS(status);
1018 }
1019
1020 UBool
isEquivalentTo(const Calendar & other) const1021 Calendar::isEquivalentTo(const Calendar& other) const
1022 {
1023 return typeid(*this) == typeid(other) &&
1024 fLenient == other.fLenient &&
1025 fRepeatedWallTime == other.fRepeatedWallTime &&
1026 fSkippedWallTime == other.fSkippedWallTime &&
1027 fFirstDayOfWeek == other.fFirstDayOfWeek &&
1028 fMinimalDaysInFirstWeek == other.fMinimalDaysInFirstWeek &&
1029 fWeekendOnset == other.fWeekendOnset &&
1030 fWeekendOnsetMillis == other.fWeekendOnsetMillis &&
1031 fWeekendCease == other.fWeekendCease &&
1032 fWeekendCeaseMillis == other.fWeekendCeaseMillis &&
1033 *fZone == *other.fZone;
1034 }
1035
1036 // -------------------------------------
1037
1038 UBool
equals(const Calendar & when,UErrorCode & status) const1039 Calendar::equals(const Calendar& when, UErrorCode& status) const
1040 {
1041 return (this == &when ||
1042 getTime(status) == when.getTime(status));
1043 }
1044
1045 // -------------------------------------
1046
1047 UBool
before(const Calendar & when,UErrorCode & status) const1048 Calendar::before(const Calendar& when, UErrorCode& status) const
1049 {
1050 return (this != &when &&
1051 getTimeInMillis(status) < when.getTimeInMillis(status));
1052 }
1053
1054 // -------------------------------------
1055
1056 UBool
after(const Calendar & when,UErrorCode & status) const1057 Calendar::after(const Calendar& when, UErrorCode& status) const
1058 {
1059 return (this != &when &&
1060 getTimeInMillis(status) > when.getTimeInMillis(status));
1061 }
1062
1063 // -------------------------------------
1064
1065
1066 const Locale* U_EXPORT2
getAvailableLocales(int32_t & count)1067 Calendar::getAvailableLocales(int32_t& count)
1068 {
1069 return Locale::getAvailableLocales(count);
1070 }
1071
1072 // -------------------------------------
1073
1074 StringEnumeration* U_EXPORT2
getKeywordValuesForLocale(const char * key,const Locale & locale,UBool commonlyUsed,UErrorCode & status)1075 Calendar::getKeywordValuesForLocale(const char* key,
1076 const Locale& locale, UBool commonlyUsed, UErrorCode& status)
1077 {
1078 // This is a wrapper over ucal_getKeywordValuesForLocale
1079 UEnumeration *uenum = ucal_getKeywordValuesForLocale(key, locale.getName(),
1080 commonlyUsed, &status);
1081 if (U_FAILURE(status)) {
1082 uenum_close(uenum);
1083 return NULL;
1084 }
1085 return new UStringEnumeration(uenum);
1086 }
1087
1088 // -------------------------------------
1089
1090 UDate U_EXPORT2
getNow()1091 Calendar::getNow()
1092 {
1093 return uprv_getUTCtime(); // return as milliseconds
1094 }
1095
1096 // -------------------------------------
1097
1098 /**
1099 * Gets this Calendar's current time as a long.
1100 * @return the current time as UTC milliseconds from the epoch.
1101 */
1102 double
getTimeInMillis(UErrorCode & status) const1103 Calendar::getTimeInMillis(UErrorCode& status) const
1104 {
1105 if(U_FAILURE(status))
1106 return 0.0;
1107
1108 if ( ! fIsTimeSet)
1109 ((Calendar*)this)->updateTime(status);
1110
1111 /* Test for buffer overflows */
1112 if(U_FAILURE(status)) {
1113 return 0.0;
1114 }
1115 return fTime;
1116 }
1117
1118 // -------------------------------------
1119
1120 /**
1121 * Sets this Calendar's current time from the given long value.
1122 * A status of U_ILLEGAL_ARGUMENT_ERROR is set when millis is
1123 * outside the range permitted by a Calendar object when not in lenient mode.
1124 * when in lenient mode the out of range values are pinned to their respective min/max.
1125 * @param date the new time in UTC milliseconds from the epoch.
1126 */
1127 void
setTimeInMillis(double millis,UErrorCode & status)1128 Calendar::setTimeInMillis( double millis, UErrorCode& status ) {
1129 if(U_FAILURE(status))
1130 return;
1131
1132 if (millis > MAX_MILLIS) {
1133 if(isLenient()) {
1134 millis = MAX_MILLIS;
1135 } else {
1136 status = U_ILLEGAL_ARGUMENT_ERROR;
1137 return;
1138 }
1139 } else if (millis < MIN_MILLIS) {
1140 if(isLenient()) {
1141 millis = MIN_MILLIS;
1142 } else {
1143 status = U_ILLEGAL_ARGUMENT_ERROR;
1144 return;
1145 }
1146 }
1147
1148 fTime = millis;
1149 fAreFieldsSet = fAreAllFieldsSet = FALSE;
1150 fIsTimeSet = fAreFieldsVirtuallySet = TRUE;
1151
1152 for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
1153 fFields[i] = 0;
1154 fStamp[i] = kUnset;
1155 fIsSet[i] = FALSE;
1156 }
1157
1158
1159 }
1160
1161 // -------------------------------------
1162
1163 int32_t
get(UCalendarDateFields field,UErrorCode & status) const1164 Calendar::get(UCalendarDateFields field, UErrorCode& status) const
1165 {
1166 // field values are only computed when actually requested; for more on when computation
1167 // of various things happens, see the "data flow in Calendar" description at the top
1168 // of this file
1169 if (U_SUCCESS(status)) ((Calendar*)this)->complete(status); // Cast away const
1170 return U_SUCCESS(status) ? fFields[field] : 0;
1171 }
1172
1173 // -------------------------------------
1174
1175 void
set(UCalendarDateFields field,int32_t value)1176 Calendar::set(UCalendarDateFields field, int32_t value)
1177 {
1178 if (fAreFieldsVirtuallySet) {
1179 UErrorCode ec = U_ZERO_ERROR;
1180 computeFields(ec);
1181 }
1182 fFields[field] = value;
1183 /* Ensure that the fNextStamp value doesn't go pass max value for int32_t */
1184 if (fNextStamp == STAMP_MAX) {
1185 recalculateStamp();
1186 }
1187 fStamp[field] = fNextStamp++;
1188 fIsSet[field] = TRUE; // Remove later
1189 fIsTimeSet = fAreFieldsSet = fAreFieldsVirtuallySet = FALSE;
1190 }
1191
1192 // -------------------------------------
1193
1194 void
set(int32_t year,int32_t month,int32_t date)1195 Calendar::set(int32_t year, int32_t month, int32_t date)
1196 {
1197 set(UCAL_YEAR, year);
1198 set(UCAL_MONTH, month);
1199 set(UCAL_DATE, date);
1200 }
1201
1202 // -------------------------------------
1203
1204 void
set(int32_t year,int32_t month,int32_t date,int32_t hour,int32_t minute)1205 Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute)
1206 {
1207 set(UCAL_YEAR, year);
1208 set(UCAL_MONTH, month);
1209 set(UCAL_DATE, date);
1210 set(UCAL_HOUR_OF_DAY, hour);
1211 set(UCAL_MINUTE, minute);
1212 }
1213
1214 // -------------------------------------
1215
1216 void
set(int32_t year,int32_t month,int32_t date,int32_t hour,int32_t minute,int32_t second)1217 Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute, int32_t second)
1218 {
1219 set(UCAL_YEAR, year);
1220 set(UCAL_MONTH, month);
1221 set(UCAL_DATE, date);
1222 set(UCAL_HOUR_OF_DAY, hour);
1223 set(UCAL_MINUTE, minute);
1224 set(UCAL_SECOND, second);
1225 }
1226
1227 // -------------------------------------
1228 // For now the full getRelatedYear implementation is here;
1229 // per #10752 move the non-default implementation to subclasses
1230 // (default implementation will do no year adjustment)
1231
gregoYearFromIslamicStart(int32_t year)1232 static int32_t gregoYearFromIslamicStart(int32_t year) {
1233 // ad hoc conversion, improve under #10752
1234 // rough est for now, ok for grego 1846-2138,
1235 // otherwise occasionally wrong (for 3% of years)
1236 int cycle, offset, shift = 0;
1237 if (year >= 1397) {
1238 cycle = (year - 1397) / 67;
1239 offset = (year - 1397) % 67;
1240 shift = 2*cycle + ((offset >= 33)? 1: 0);
1241 } else {
1242 cycle = (year - 1396) / 67 - 1;
1243 offset = -(year - 1396) % 67;
1244 shift = 2*cycle + ((offset <= 33)? 1: 0);
1245 }
1246 return year + 579 - shift;
1247 }
1248
getRelatedYear(UErrorCode & status) const1249 int32_t Calendar::getRelatedYear(UErrorCode &status) const
1250 {
1251 if (U_FAILURE(status)) {
1252 return 0;
1253 }
1254 int32_t year = get(UCAL_EXTENDED_YEAR, status);
1255 if (U_FAILURE(status)) {
1256 return 0;
1257 }
1258 // modify for calendar type
1259 ECalType type = getCalendarType(getType());
1260 switch (type) {
1261 case CALTYPE_PERSIAN:
1262 year += 622; break;
1263 case CALTYPE_HEBREW:
1264 year -= 3760; break;
1265 case CALTYPE_CHINESE:
1266 year -= 2637; break;
1267 case CALTYPE_INDIAN:
1268 year += 79; break;
1269 case CALTYPE_COPTIC:
1270 year += 284; break;
1271 case CALTYPE_ETHIOPIC:
1272 year += 8; break;
1273 case CALTYPE_ETHIOPIC_AMETE_ALEM:
1274 year -=5492; break;
1275 case CALTYPE_DANGI:
1276 year -= 2333; break;
1277 case CALTYPE_ISLAMIC_CIVIL:
1278 case CALTYPE_ISLAMIC:
1279 case CALTYPE_ISLAMIC_UMALQURA:
1280 case CALTYPE_ISLAMIC_TBLA:
1281 case CALTYPE_ISLAMIC_RGSA:
1282 year = gregoYearFromIslamicStart(year); break;
1283 default:
1284 // CALTYPE_GREGORIAN
1285 // CALTYPE_JAPANESE
1286 // CALTYPE_BUDDHIST
1287 // CALTYPE_ROC
1288 // CALTYPE_ISO8601
1289 // do nothing, EXTENDED_YEAR same as Gregorian
1290 break;
1291 }
1292 return year;
1293 }
1294
1295 // -------------------------------------
1296 // For now the full setRelatedYear implementation is here;
1297 // per #10752 move the non-default implementation to subclasses
1298 // (default implementation will do no year adjustment)
1299
firstIslamicStartYearFromGrego(int32_t year)1300 static int32_t firstIslamicStartYearFromGrego(int32_t year) {
1301 // ad hoc conversion, improve under #10752
1302 // rough est for now, ok for grego 1846-2138,
1303 // otherwise occasionally wrong (for 3% of years)
1304 int cycle, offset, shift = 0;
1305 if (year >= 1977) {
1306 cycle = (year - 1977) / 65;
1307 offset = (year - 1977) % 65;
1308 shift = 2*cycle + ((offset >= 32)? 1: 0);
1309 } else {
1310 cycle = (year - 1976) / 65 - 1;
1311 offset = -(year - 1976) % 65;
1312 shift = 2*cycle + ((offset <= 32)? 1: 0);
1313 }
1314 return year - 579 + shift;
1315 }
setRelatedYear(int32_t year)1316 void Calendar::setRelatedYear(int32_t year)
1317 {
1318 // modify for calendar type
1319 ECalType type = getCalendarType(getType());
1320 switch (type) {
1321 case CALTYPE_PERSIAN:
1322 year -= 622; break;
1323 case CALTYPE_HEBREW:
1324 year += 3760; break;
1325 case CALTYPE_CHINESE:
1326 year += 2637; break;
1327 case CALTYPE_INDIAN:
1328 year -= 79; break;
1329 case CALTYPE_COPTIC:
1330 year -= 284; break;
1331 case CALTYPE_ETHIOPIC:
1332 year -= 8; break;
1333 case CALTYPE_ETHIOPIC_AMETE_ALEM:
1334 year +=5492; break;
1335 case CALTYPE_DANGI:
1336 year += 2333; break;
1337 case CALTYPE_ISLAMIC_CIVIL:
1338 case CALTYPE_ISLAMIC:
1339 case CALTYPE_ISLAMIC_UMALQURA:
1340 case CALTYPE_ISLAMIC_TBLA:
1341 case CALTYPE_ISLAMIC_RGSA:
1342 year = firstIslamicStartYearFromGrego(year); break;
1343 default:
1344 // CALTYPE_GREGORIAN
1345 // CALTYPE_JAPANESE
1346 // CALTYPE_BUDDHIST
1347 // CALTYPE_ROC
1348 // CALTYPE_ISO8601
1349 // do nothing, EXTENDED_YEAR same as Gregorian
1350 break;
1351 }
1352 // set extended year
1353 set(UCAL_EXTENDED_YEAR, year);
1354 }
1355
1356 // -------------------------------------
1357
1358 void
clear()1359 Calendar::clear()
1360 {
1361 for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
1362 fFields[i] = 0; // Must do this; other code depends on it
1363 fStamp[i] = kUnset;
1364 fIsSet[i] = FALSE; // Remove later
1365 }
1366 fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE;
1367 // fTime is not 'cleared' - may be used if no fields are set.
1368 }
1369
1370 // -------------------------------------
1371
1372 void
clear(UCalendarDateFields field)1373 Calendar::clear(UCalendarDateFields field)
1374 {
1375 if (fAreFieldsVirtuallySet) {
1376 UErrorCode ec = U_ZERO_ERROR;
1377 computeFields(ec);
1378 }
1379 fFields[field] = 0;
1380 fStamp[field] = kUnset;
1381 fIsSet[field] = FALSE; // Remove later
1382 fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE;
1383 }
1384
1385 // -------------------------------------
1386
1387 UBool
isSet(UCalendarDateFields field) const1388 Calendar::isSet(UCalendarDateFields field) const
1389 {
1390 return fAreFieldsVirtuallySet || (fStamp[field] != kUnset);
1391 }
1392
1393
newestStamp(UCalendarDateFields first,UCalendarDateFields last,int32_t bestStampSoFar) const1394 int32_t Calendar::newestStamp(UCalendarDateFields first, UCalendarDateFields last, int32_t bestStampSoFar) const
1395 {
1396 int32_t bestStamp = bestStampSoFar;
1397 for (int32_t i=(int32_t)first; i<=(int32_t)last; ++i) {
1398 if (fStamp[i] > bestStamp) {
1399 bestStamp = fStamp[i];
1400 }
1401 }
1402 return bestStamp;
1403 }
1404
1405
1406 // -------------------------------------
1407
1408 void
complete(UErrorCode & status)1409 Calendar::complete(UErrorCode& status)
1410 {
1411 if (!fIsTimeSet) {
1412 updateTime(status);
1413 /* Test for buffer overflows */
1414 if(U_FAILURE(status)) {
1415 return;
1416 }
1417 }
1418 if (!fAreFieldsSet) {
1419 computeFields(status); // fills in unset fields
1420 /* Test for buffer overflows */
1421 if(U_FAILURE(status)) {
1422 return;
1423 }
1424 fAreFieldsSet = TRUE;
1425 fAreAllFieldsSet = TRUE;
1426 }
1427 }
1428
1429 //-------------------------------------------------------------------------
1430 // Protected utility methods for use by subclasses. These are very handy
1431 // for implementing add, roll, and computeFields.
1432 //-------------------------------------------------------------------------
1433
1434 /**
1435 * Adjust the specified field so that it is within
1436 * the allowable range for the date to which this calendar is set.
1437 * For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH}
1438 * field for a calendar set to April 31 would cause it to be set
1439 * to April 30.
1440 * <p>
1441 * <b>Subclassing:</b>
1442 * <br>
1443 * This utility method is intended for use by subclasses that need to implement
1444 * their own overrides of {@link #roll roll} and {@link #add add}.
1445 * <p>
1446 * <b>Note:</b>
1447 * <code>pinField</code> is implemented in terms of
1448 * {@link #getActualMinimum getActualMinimum}
1449 * and {@link #getActualMaximum getActualMaximum}. If either of those methods uses
1450 * a slow, iterative algorithm for a particular field, it would be
1451 * unwise to attempt to call <code>pinField</code> for that field. If you
1452 * really do need to do so, you should override this method to do
1453 * something more efficient for that field.
1454 * <p>
1455 * @param field The calendar field whose value should be pinned.
1456 *
1457 * @see #getActualMinimum
1458 * @see #getActualMaximum
1459 * @stable ICU 2.0
1460 */
pinField(UCalendarDateFields field,UErrorCode & status)1461 void Calendar::pinField(UCalendarDateFields field, UErrorCode& status) {
1462 int32_t max = getActualMaximum(field, status);
1463 int32_t min = getActualMinimum(field, status);
1464
1465 if (fFields[field] > max) {
1466 set(field, max);
1467 } else if (fFields[field] < min) {
1468 set(field, min);
1469 }
1470 }
1471
1472
computeFields(UErrorCode & ec)1473 void Calendar::computeFields(UErrorCode &ec)
1474 {
1475 if (U_FAILURE(ec)) {
1476 return;
1477 }
1478 // Compute local wall millis
1479 double localMillis = internalGetTime();
1480 int32_t rawOffset, dstOffset;
1481 getTimeZone().getOffset(localMillis, FALSE, rawOffset, dstOffset, ec);
1482 localMillis += (rawOffset + dstOffset);
1483
1484 // Mark fields as set. Do this before calling handleComputeFields().
1485 uint32_t mask = //fInternalSetMask;
1486 (1 << UCAL_ERA) |
1487 (1 << UCAL_YEAR) |
1488 (1 << UCAL_MONTH) |
1489 (1 << UCAL_DAY_OF_MONTH) | // = UCAL_DATE
1490 (1 << UCAL_DAY_OF_YEAR) |
1491 (1 << UCAL_EXTENDED_YEAR);
1492
1493 for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
1494 if ((mask & 1) == 0) {
1495 fStamp[i] = kInternallySet;
1496 fIsSet[i] = TRUE; // Remove later
1497 } else {
1498 fStamp[i] = kUnset;
1499 fIsSet[i] = FALSE; // Remove later
1500 }
1501 mask >>= 1;
1502 }
1503
1504 // We used to check for and correct extreme millis values (near
1505 // Long.MIN_VALUE or Long.MAX_VALUE) here. Such values would cause
1506 // overflows from positive to negative (or vice versa) and had to
1507 // be manually tweaked. We no longer need to do this because we
1508 // have limited the range of supported dates to those that have a
1509 // Julian day that fits into an int. This allows us to implement a
1510 // JULIAN_DAY field and also removes some inelegant code. - Liu
1511 // 11/6/00
1512
1513 int32_t days = (int32_t)ClockMath::floorDivide(localMillis, (double)kOneDay);
1514
1515 internalSet(UCAL_JULIAN_DAY,days + kEpochStartAsJulianDay);
1516
1517 #if defined (U_DEBUG_CAL)
1518 //fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n",
1519 //__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
1520 #endif
1521
1522 computeGregorianAndDOWFields(fFields[UCAL_JULIAN_DAY], ec);
1523
1524 // Call framework method to have subclass compute its fields.
1525 // These must include, at a minimum, MONTH, DAY_OF_MONTH,
1526 // EXTENDED_YEAR, YEAR, DAY_OF_YEAR. This method will call internalSet(),
1527 // which will update stamp[].
1528 handleComputeFields(fFields[UCAL_JULIAN_DAY], ec);
1529
1530 // Compute week-related fields, based on the subclass-computed
1531 // fields computed by handleComputeFields().
1532 computeWeekFields(ec);
1533
1534 // Compute time-related fields. These are indepent of the date and
1535 // of the subclass algorithm. They depend only on the local zone
1536 // wall milliseconds in day.
1537 int32_t millisInDay = (int32_t) (localMillis - (days * kOneDay));
1538 fFields[UCAL_MILLISECONDS_IN_DAY] = millisInDay;
1539 fFields[UCAL_MILLISECOND] = millisInDay % 1000;
1540 millisInDay /= 1000;
1541 fFields[UCAL_SECOND] = millisInDay % 60;
1542 millisInDay /= 60;
1543 fFields[UCAL_MINUTE] = millisInDay % 60;
1544 millisInDay /= 60;
1545 fFields[UCAL_HOUR_OF_DAY] = millisInDay;
1546 fFields[UCAL_AM_PM] = millisInDay / 12; // Assume AM == 0
1547 fFields[UCAL_HOUR] = millisInDay % 12;
1548 fFields[UCAL_ZONE_OFFSET] = rawOffset;
1549 fFields[UCAL_DST_OFFSET] = dstOffset;
1550 }
1551
julianDayToDayOfWeek(double julian)1552 uint8_t Calendar::julianDayToDayOfWeek(double julian)
1553 {
1554 // If julian is negative, then julian%7 will be negative, so we adjust
1555 // accordingly. We add 1 because Julian day 0 is Monday.
1556 int8_t dayOfWeek = (int8_t) uprv_fmod(julian + 1, 7);
1557
1558 uint8_t result = (uint8_t)(dayOfWeek + ((dayOfWeek < 0) ? (7+UCAL_SUNDAY ) : UCAL_SUNDAY));
1559 return result;
1560 }
1561
1562 /**
1563 * Compute the Gregorian calendar year, month, and day of month from
1564 * the given Julian day. These values are not stored in fields, but in
1565 * member variables gregorianXxx. Also compute the DAY_OF_WEEK and
1566 * DOW_LOCAL fields.
1567 */
computeGregorianAndDOWFields(int32_t julianDay,UErrorCode & ec)1568 void Calendar::computeGregorianAndDOWFields(int32_t julianDay, UErrorCode &ec)
1569 {
1570 computeGregorianFields(julianDay, ec);
1571
1572 // Compute day of week: JD 0 = Monday
1573 int32_t dow = julianDayToDayOfWeek(julianDay);
1574 internalSet(UCAL_DAY_OF_WEEK,dow);
1575
1576 // Calculate 1-based localized day of week
1577 int32_t dowLocal = dow - getFirstDayOfWeek() + 1;
1578 if (dowLocal < 1) {
1579 dowLocal += 7;
1580 }
1581 internalSet(UCAL_DOW_LOCAL,dowLocal);
1582 fFields[UCAL_DOW_LOCAL] = dowLocal;
1583 }
1584
1585 /**
1586 * Compute the Gregorian calendar year, month, and day of month from the
1587 * Julian day. These values are not stored in fields, but in member
1588 * variables gregorianXxx. They are used for time zone computations and by
1589 * subclasses that are Gregorian derivatives. Subclasses may call this
1590 * method to perform a Gregorian calendar millis->fields computation.
1591 */
computeGregorianFields(int32_t julianDay,UErrorCode &)1592 void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode & /* ec */) {
1593 int32_t gregorianDayOfWeekUnused;
1594 Grego::dayToFields(julianDay - kEpochStartAsJulianDay, fGregorianYear, fGregorianMonth, fGregorianDayOfMonth, gregorianDayOfWeekUnused, fGregorianDayOfYear);
1595 }
1596
1597 /**
1598 * Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH,
1599 * DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR,
1600 * DAY_OF_WEEK, and DAY_OF_YEAR. The latter fields are computed by the
1601 * subclass based on the calendar system.
1602 *
1603 * <p>The YEAR_WOY field is computed simplistically. It is equal to YEAR
1604 * most of the time, but at the year boundary it may be adjusted to YEAR-1
1605 * or YEAR+1 to reflect the overlap of a week into an adjacent year. In
1606 * this case, a simple increment or decrement is performed on YEAR, even
1607 * though this may yield an invalid YEAR value. For instance, if the YEAR
1608 * is part of a calendar system with an N-year cycle field CYCLE, then
1609 * incrementing the YEAR may involve incrementing CYCLE and setting YEAR
1610 * back to 0 or 1. This is not handled by this code, and in fact cannot be
1611 * simply handled without having subclasses define an entire parallel set of
1612 * fields for fields larger than or equal to a year. This additional
1613 * complexity is not warranted, since the intention of the YEAR_WOY field is
1614 * to support ISO 8601 notation, so it will typically be used with a
1615 * proleptic Gregorian calendar, which has no field larger than a year.
1616 */
computeWeekFields(UErrorCode & ec)1617 void Calendar::computeWeekFields(UErrorCode &ec) {
1618 if(U_FAILURE(ec)) {
1619 return;
1620 }
1621 int32_t eyear = fFields[UCAL_EXTENDED_YEAR];
1622 int32_t dayOfWeek = fFields[UCAL_DAY_OF_WEEK];
1623 int32_t dayOfYear = fFields[UCAL_DAY_OF_YEAR];
1624
1625 // WEEK_OF_YEAR start
1626 // Compute the week of the year. For the Gregorian calendar, valid week
1627 // numbers run from 1 to 52 or 53, depending on the year, the first day
1628 // of the week, and the minimal days in the first week. For other
1629 // calendars, the valid range may be different -- it depends on the year
1630 // length. Days at the start of the year may fall into the last week of
1631 // the previous year; days at the end of the year may fall into the
1632 // first week of the next year. ASSUME that the year length is less than
1633 // 7000 days.
1634 int32_t yearOfWeekOfYear = eyear;
1635 int32_t relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; // 0..6
1636 int32_t relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; // 0..6
1637 int32_t woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53
1638 if ((7 - relDowJan1) >= getMinimalDaysInFirstWeek()) {
1639 ++woy;
1640 }
1641
1642 // Adjust for weeks at the year end that overlap into the previous or
1643 // next calendar year.
1644 if (woy == 0) {
1645 // We are the last week of the previous year.
1646 // Check to see if we are in the last week; if so, we need
1647 // to handle the case in which we are the first week of the
1648 // next year.
1649
1650 int32_t prevDoy = dayOfYear + handleGetYearLength(eyear - 1);
1651 woy = weekNumber(prevDoy, dayOfWeek);
1652 yearOfWeekOfYear--;
1653 } else {
1654 int32_t lastDoy = handleGetYearLength(eyear);
1655 // Fast check: For it to be week 1 of the next year, the DOY
1656 // must be on or after L-5, where L is yearLength(), then it
1657 // cannot possibly be week 1 of the next year:
1658 // L-5 L
1659 // doy: 359 360 361 362 363 364 365 001
1660 // dow: 1 2 3 4 5 6 7
1661 if (dayOfYear >= (lastDoy - 5)) {
1662 int32_t lastRelDow = (relDow + lastDoy - dayOfYear) % 7;
1663 if (lastRelDow < 0) {
1664 lastRelDow += 7;
1665 }
1666 if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) &&
1667 ((dayOfYear + 7 - relDow) > lastDoy)) {
1668 woy = 1;
1669 yearOfWeekOfYear++;
1670 }
1671 }
1672 }
1673 fFields[UCAL_WEEK_OF_YEAR] = woy;
1674 fFields[UCAL_YEAR_WOY] = yearOfWeekOfYear;
1675 // WEEK_OF_YEAR end
1676
1677 int32_t dayOfMonth = fFields[UCAL_DAY_OF_MONTH];
1678 fFields[UCAL_WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek);
1679 fFields[UCAL_DAY_OF_WEEK_IN_MONTH] = (dayOfMonth-1) / 7 + 1;
1680 #if defined (U_DEBUG_CAL)
1681 if(fFields[UCAL_DAY_OF_WEEK_IN_MONTH]==0) fprintf(stderr, "%s:%d: DOWIM %d on %g\n",
1682 __FILE__, __LINE__,fFields[UCAL_DAY_OF_WEEK_IN_MONTH], fTime);
1683 #endif
1684 }
1685
1686
weekNumber(int32_t desiredDay,int32_t dayOfPeriod,int32_t dayOfWeek)1687 int32_t Calendar::weekNumber(int32_t desiredDay, int32_t dayOfPeriod, int32_t dayOfWeek)
1688 {
1689 // Determine the day of the week of the first day of the period
1690 // in question (either a year or a month). Zero represents the
1691 // first day of the week on this calendar.
1692 int32_t periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % 7;
1693 if (periodStartDayOfWeek < 0) periodStartDayOfWeek += 7;
1694
1695 // Compute the week number. Initially, ignore the first week, which
1696 // may be fractional (or may not be). We add periodStartDayOfWeek in
1697 // order to fill out the first week, if it is fractional.
1698 int32_t weekNo = (desiredDay + periodStartDayOfWeek - 1)/7;
1699
1700 // If the first week is long enough, then count it. If
1701 // the minimal days in the first week is one, or if the period start
1702 // is zero, we always increment weekNo.
1703 if ((7 - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo;
1704
1705 return weekNo;
1706 }
1707
handleComputeFields(int32_t,UErrorCode &)1708 void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode &/* status */)
1709 {
1710 internalSet(UCAL_MONTH, getGregorianMonth());
1711 internalSet(UCAL_DAY_OF_MONTH, getGregorianDayOfMonth());
1712 internalSet(UCAL_DAY_OF_YEAR, getGregorianDayOfYear());
1713 int32_t eyear = getGregorianYear();
1714 internalSet(UCAL_EXTENDED_YEAR, eyear);
1715 int32_t era = GregorianCalendar::AD;
1716 if (eyear < 1) {
1717 era = GregorianCalendar::BC;
1718 eyear = 1 - eyear;
1719 }
1720 internalSet(UCAL_ERA, era);
1721 internalSet(UCAL_YEAR, eyear);
1722 }
1723 // -------------------------------------
1724
1725
roll(EDateFields field,int32_t amount,UErrorCode & status)1726 void Calendar::roll(EDateFields field, int32_t amount, UErrorCode& status)
1727 {
1728 roll((UCalendarDateFields)field, amount, status);
1729 }
1730
roll(UCalendarDateFields field,int32_t amount,UErrorCode & status)1731 void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status)
1732 {
1733 if (amount == 0) {
1734 return; // Nothing to do
1735 }
1736
1737 complete(status);
1738
1739 if(U_FAILURE(status)) {
1740 return;
1741 }
1742 switch (field) {
1743 case UCAL_DAY_OF_MONTH:
1744 case UCAL_AM_PM:
1745 case UCAL_MINUTE:
1746 case UCAL_SECOND:
1747 case UCAL_MILLISECOND:
1748 case UCAL_MILLISECONDS_IN_DAY:
1749 case UCAL_ERA:
1750 // These are the standard roll instructions. These work for all
1751 // simple cases, that is, cases in which the limits are fixed, such
1752 // as the hour, the day of the month, and the era.
1753 {
1754 int32_t min = getActualMinimum(field,status);
1755 int32_t max = getActualMaximum(field,status);
1756 int32_t gap = max - min + 1;
1757
1758 int32_t value = internalGet(field) + amount;
1759 value = (value - min) % gap;
1760 if (value < 0) {
1761 value += gap;
1762 }
1763 value += min;
1764
1765 set(field, value);
1766 return;
1767 }
1768
1769 case UCAL_HOUR:
1770 case UCAL_HOUR_OF_DAY:
1771 // Rolling the hour is difficult on the ONSET and CEASE days of
1772 // daylight savings. For example, if the change occurs at
1773 // 2 AM, we have the following progression:
1774 // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst
1775 // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std
1776 // To get around this problem we don't use fields; we manipulate
1777 // the time in millis directly.
1778 {
1779 // Assume min == 0 in calculations below
1780 double start = getTimeInMillis(status);
1781 int32_t oldHour = internalGet(field);
1782 int32_t max = getMaximum(field);
1783 int32_t newHour = (oldHour + amount) % (max + 1);
1784 if (newHour < 0) {
1785 newHour += max + 1;
1786 }
1787 setTimeInMillis(start + kOneHour * (newHour - oldHour),status);
1788 return;
1789 }
1790
1791 case UCAL_MONTH:
1792 // Rolling the month involves both pinning the final value
1793 // and adjusting the DAY_OF_MONTH if necessary. We only adjust the
1794 // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
1795 // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
1796 {
1797 int32_t max = getActualMaximum(UCAL_MONTH, status);
1798 int32_t mon = (internalGet(UCAL_MONTH) + amount) % (max+1);
1799
1800 if (mon < 0) {
1801 mon += (max + 1);
1802 }
1803 set(UCAL_MONTH, mon);
1804
1805 // Keep the day of month in range. We don't want to spill over
1806 // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 ->
1807 // mar3.
1808 pinField(UCAL_DAY_OF_MONTH,status);
1809 return;
1810 }
1811
1812 case UCAL_YEAR:
1813 case UCAL_YEAR_WOY:
1814 {
1815 // * If era==0 and years go backwards in time, change sign of amount.
1816 // * Until we have new API per #9393, we temporarily hardcode knowledge of
1817 // which calendars have era 0 years that go backwards.
1818 UBool era0WithYearsThatGoBackwards = FALSE;
1819 int32_t era = get(UCAL_ERA, status);
1820 if (era == 0) {
1821 const char * calType = getType();
1822 if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) {
1823 amount = -amount;
1824 era0WithYearsThatGoBackwards = TRUE;
1825 }
1826 }
1827 int32_t newYear = internalGet(field) + amount;
1828 if (era > 0 || newYear >= 1) {
1829 int32_t maxYear = getActualMaximum(field, status);
1830 if (maxYear < 32768) {
1831 // this era has real bounds, roll should wrap years
1832 if (newYear < 1) {
1833 newYear = maxYear - ((-newYear) % maxYear);
1834 } else if (newYear > maxYear) {
1835 newYear = ((newYear - 1) % maxYear) + 1;
1836 }
1837 // else era is unbounded, just pin low year instead of wrapping
1838 } else if (newYear < 1) {
1839 newYear = 1;
1840 }
1841 // else we are in era 0 with newYear < 1;
1842 // calendars with years that go backwards must pin the year value at 0,
1843 // other calendars can have years < 0 in era 0
1844 } else if (era0WithYearsThatGoBackwards) {
1845 newYear = 1;
1846 }
1847 set(field, newYear);
1848 pinField(UCAL_MONTH,status);
1849 pinField(UCAL_DAY_OF_MONTH,status);
1850 return;
1851 }
1852
1853 case UCAL_EXTENDED_YEAR:
1854 // Rolling the year can involve pinning the DAY_OF_MONTH.
1855 set(field, internalGet(field) + amount);
1856 pinField(UCAL_MONTH,status);
1857 pinField(UCAL_DAY_OF_MONTH,status);
1858 return;
1859
1860 case UCAL_WEEK_OF_MONTH:
1861 {
1862 // This is tricky, because during the roll we may have to shift
1863 // to a different day of the week. For example:
1864
1865 // s m t w r f s
1866 // 1 2 3 4 5
1867 // 6 7 8 9 10 11 12
1868
1869 // When rolling from the 6th or 7th back one week, we go to the
1870 // 1st (assuming that the first partial week counts). The same
1871 // thing happens at the end of the month.
1872
1873 // The other tricky thing is that we have to figure out whether
1874 // the first partial week actually counts or not, based on the
1875 // minimal first days in the week. And we have to use the
1876 // correct first day of the week to delineate the week
1877 // boundaries.
1878
1879 // Here's our algorithm. First, we find the real boundaries of
1880 // the month. Then we discard the first partial week if it
1881 // doesn't count in this locale. Then we fill in the ends with
1882 // phantom days, so that the first partial week and the last
1883 // partial week are full weeks. We then have a nice square
1884 // block of weeks. We do the usual rolling within this block,
1885 // as is done elsewhere in this method. If we wind up on one of
1886 // the phantom days that we added, we recognize this and pin to
1887 // the first or the last day of the month. Easy, eh?
1888
1889 // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
1890 // in this locale. We have dow in 0..6.
1891 int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
1892 if (dow < 0) dow += 7;
1893
1894 // Find the day of the week (normalized for locale) for the first
1895 // of the month.
1896 int32_t fdm = (dow - internalGet(UCAL_DAY_OF_MONTH) + 1) % 7;
1897 if (fdm < 0) fdm += 7;
1898
1899 // Get the first day of the first full week of the month,
1900 // including phantom days, if any. Figure out if the first week
1901 // counts or not; if it counts, then fill in phantom days. If
1902 // not, advance to the first real full week (skip the partial week).
1903 int32_t start;
1904 if ((7 - fdm) < getMinimalDaysInFirstWeek())
1905 start = 8 - fdm; // Skip the first partial week
1906 else
1907 start = 1 - fdm; // This may be zero or negative
1908
1909 // Get the day of the week (normalized for locale) for the last
1910 // day of the month.
1911 int32_t monthLen = getActualMaximum(UCAL_DAY_OF_MONTH, status);
1912 int32_t ldm = (monthLen - internalGet(UCAL_DAY_OF_MONTH) + dow) % 7;
1913 // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.
1914
1915 // Get the limit day for the blocked-off rectangular month; that
1916 // is, the day which is one past the last day of the month,
1917 // after the month has already been filled in with phantom days
1918 // to fill out the last week. This day has a normalized DOW of 0.
1919 int32_t limit = monthLen + 7 - ldm;
1920
1921 // Now roll between start and (limit - 1).
1922 int32_t gap = limit - start;
1923 int32_t day_of_month = (internalGet(UCAL_DAY_OF_MONTH) + amount*7 -
1924 start) % gap;
1925 if (day_of_month < 0) day_of_month += gap;
1926 day_of_month += start;
1927
1928 // Finally, pin to the real start and end of the month.
1929 if (day_of_month < 1) day_of_month = 1;
1930 if (day_of_month > monthLen) day_of_month = monthLen;
1931
1932 // Set the DAY_OF_MONTH. We rely on the fact that this field
1933 // takes precedence over everything else (since all other fields
1934 // are also set at this point). If this fact changes (if the
1935 // disambiguation algorithm changes) then we will have to unset
1936 // the appropriate fields here so that DAY_OF_MONTH is attended
1937 // to.
1938 set(UCAL_DAY_OF_MONTH, day_of_month);
1939 return;
1940 }
1941 case UCAL_WEEK_OF_YEAR:
1942 {
1943 // This follows the outline of WEEK_OF_MONTH, except it applies
1944 // to the whole year. Please see the comment for WEEK_OF_MONTH
1945 // for general notes.
1946
1947 // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
1948 // in this locale. We have dow in 0..6.
1949 int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
1950 if (dow < 0) dow += 7;
1951
1952 // Find the day of the week (normalized for locale) for the first
1953 // of the year.
1954 int32_t fdy = (dow - internalGet(UCAL_DAY_OF_YEAR) + 1) % 7;
1955 if (fdy < 0) fdy += 7;
1956
1957 // Get the first day of the first full week of the year,
1958 // including phantom days, if any. Figure out if the first week
1959 // counts or not; if it counts, then fill in phantom days. If
1960 // not, advance to the first real full week (skip the partial week).
1961 int32_t start;
1962 if ((7 - fdy) < getMinimalDaysInFirstWeek())
1963 start = 8 - fdy; // Skip the first partial week
1964 else
1965 start = 1 - fdy; // This may be zero or negative
1966
1967 // Get the day of the week (normalized for locale) for the last
1968 // day of the year.
1969 int32_t yearLen = getActualMaximum(UCAL_DAY_OF_YEAR,status);
1970 int32_t ldy = (yearLen - internalGet(UCAL_DAY_OF_YEAR) + dow) % 7;
1971 // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here.
1972
1973 // Get the limit day for the blocked-off rectangular year; that
1974 // is, the day which is one past the last day of the year,
1975 // after the year has already been filled in with phantom days
1976 // to fill out the last week. This day has a normalized DOW of 0.
1977 int32_t limit = yearLen + 7 - ldy;
1978
1979 // Now roll between start and (limit - 1).
1980 int32_t gap = limit - start;
1981 int32_t day_of_year = (internalGet(UCAL_DAY_OF_YEAR) + amount*7 -
1982 start) % gap;
1983 if (day_of_year < 0) day_of_year += gap;
1984 day_of_year += start;
1985
1986 // Finally, pin to the real start and end of the month.
1987 if (day_of_year < 1) day_of_year = 1;
1988 if (day_of_year > yearLen) day_of_year = yearLen;
1989
1990 // Make sure that the year and day of year are attended to by
1991 // clearing other fields which would normally take precedence.
1992 // If the disambiguation algorithm is changed, this section will
1993 // have to be updated as well.
1994 set(UCAL_DAY_OF_YEAR, day_of_year);
1995 clear(UCAL_MONTH);
1996 return;
1997 }
1998 case UCAL_DAY_OF_YEAR:
1999 {
2000 // Roll the day of year using millis. Compute the millis for
2001 // the start of the year, and get the length of the year.
2002 double delta = amount * kOneDay; // Scale up from days to millis
2003 double min2 = internalGet(UCAL_DAY_OF_YEAR)-1;
2004 min2 *= kOneDay;
2005 min2 = internalGetTime() - min2;
2006
2007 // double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay;
2008 double newtime;
2009
2010 double yearLength = getActualMaximum(UCAL_DAY_OF_YEAR,status);
2011 double oneYear = yearLength;
2012 oneYear *= kOneDay;
2013 newtime = uprv_fmod((internalGetTime() + delta - min2), oneYear);
2014 if (newtime < 0) newtime += oneYear;
2015 setTimeInMillis(newtime + min2, status);
2016 return;
2017 }
2018 case UCAL_DAY_OF_WEEK:
2019 case UCAL_DOW_LOCAL:
2020 {
2021 // Roll the day of week using millis. Compute the millis for
2022 // the start of the week, using the first day of week setting.
2023 // Restrict the millis to [start, start+7days).
2024 double delta = amount * kOneDay; // Scale up from days to millis
2025 // Compute the number of days before the current day in this
2026 // week. This will be a value 0..6.
2027 int32_t leadDays = internalGet(field);
2028 leadDays -= (field == UCAL_DAY_OF_WEEK) ? getFirstDayOfWeek() : 1;
2029 if (leadDays < 0) leadDays += 7;
2030 double min2 = internalGetTime() - leadDays * kOneDay;
2031 double newtime = uprv_fmod((internalGetTime() + delta - min2), kOneWeek);
2032 if (newtime < 0) newtime += kOneWeek;
2033 setTimeInMillis(newtime + min2, status);
2034 return;
2035 }
2036 case UCAL_DAY_OF_WEEK_IN_MONTH:
2037 {
2038 // Roll the day of week in the month using millis. Determine
2039 // the first day of the week in the month, and then the last,
2040 // and then roll within that range.
2041 double delta = amount * kOneWeek; // Scale up from weeks to millis
2042 // Find the number of same days of the week before this one
2043 // in this month.
2044 int32_t preWeeks = (internalGet(UCAL_DAY_OF_MONTH) - 1) / 7;
2045 // Find the number of same days of the week after this one
2046 // in this month.
2047 int32_t postWeeks = (getActualMaximum(UCAL_DAY_OF_MONTH,status) -
2048 internalGet(UCAL_DAY_OF_MONTH)) / 7;
2049 // From these compute the min and gap millis for rolling.
2050 double min2 = internalGetTime() - preWeeks * kOneWeek;
2051 double gap2 = kOneWeek * (preWeeks + postWeeks + 1); // Must add 1!
2052 // Roll within this range
2053 double newtime = uprv_fmod((internalGetTime() + delta - min2), gap2);
2054 if (newtime < 0) newtime += gap2;
2055 setTimeInMillis(newtime + min2, status);
2056 return;
2057 }
2058 case UCAL_JULIAN_DAY:
2059 set(field, internalGet(field) + amount);
2060 return;
2061 default:
2062 // Other fields cannot be rolled by this method
2063 #if defined (U_DEBUG_CAL)
2064 fprintf(stderr, "%s:%d: ILLEGAL ARG because of roll on non-rollable field %s\n",
2065 __FILE__, __LINE__,fldName(field));
2066 #endif
2067 status = U_ILLEGAL_ARGUMENT_ERROR;
2068 }
2069 }
2070
add(EDateFields field,int32_t amount,UErrorCode & status)2071 void Calendar::add(EDateFields field, int32_t amount, UErrorCode& status)
2072 {
2073 Calendar::add((UCalendarDateFields)field, amount, status);
2074 }
2075
2076 // -------------------------------------
add(UCalendarDateFields field,int32_t amount,UErrorCode & status)2077 void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status)
2078 {
2079 if (amount == 0) {
2080 return; // Do nothing!
2081 }
2082
2083 // We handle most fields in the same way. The algorithm is to add
2084 // a computed amount of millis to the current millis. The only
2085 // wrinkle is with DST (and/or a change to the zone's UTC offset, which
2086 // we'll include with DST) -- for some fields, like the DAY_OF_MONTH,
2087 // we don't want the wall time to shift due to changes in DST. If the
2088 // result of the add operation is to move from DST to Standard, or
2089 // vice versa, we need to adjust by an hour forward or back,
2090 // respectively. For such fields we set keepWallTimeInvariant to TRUE.
2091
2092 // We only adjust the DST for fields larger than an hour. For
2093 // fields smaller than an hour, we cannot adjust for DST without
2094 // causing problems. for instance, if you add one hour to April 5,
2095 // 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an
2096 // illegal value), but then the adjustment sees the change and
2097 // compensates by subtracting an hour. As a result the time
2098 // doesn't advance at all.
2099
2100 // For some fields larger than a day, such as a UCAL_MONTH, we pin the
2101 // UCAL_DAY_OF_MONTH. This allows <March 31>.add(UCAL_MONTH, 1) to be
2102 // <April 30>, rather than <April 31> => <May 1>.
2103
2104 double delta = amount; // delta in ms
2105 UBool keepWallTimeInvariant = TRUE;
2106
2107 switch (field) {
2108 case UCAL_ERA:
2109 set(field, get(field, status) + amount);
2110 pinField(UCAL_ERA, status);
2111 return;
2112
2113 case UCAL_YEAR:
2114 case UCAL_YEAR_WOY:
2115 {
2116 // * If era=0 and years go backwards in time, change sign of amount.
2117 // * Until we have new API per #9393, we temporarily hardcode knowledge of
2118 // which calendars have era 0 years that go backwards.
2119 // * Note that for UCAL_YEAR (but not UCAL_YEAR_WOY) we could instead handle
2120 // this by applying the amount to the UCAL_EXTENDED_YEAR field; but since
2121 // we would still need to handle UCAL_YEAR_WOY as below, might as well
2122 // also handle UCAL_YEAR the same way.
2123 int32_t era = get(UCAL_ERA, status);
2124 if (era == 0) {
2125 const char * calType = getType();
2126 if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) {
2127 amount = -amount;
2128 }
2129 }
2130 }
2131 // Fall through into normal handling
2132 U_FALLTHROUGH;
2133 case UCAL_EXTENDED_YEAR:
2134 case UCAL_MONTH:
2135 {
2136 UBool oldLenient = isLenient();
2137 setLenient(TRUE);
2138 set(field, get(field, status) + amount);
2139 pinField(UCAL_DAY_OF_MONTH, status);
2140 if(oldLenient==FALSE) {
2141 complete(status); /* force recalculate */
2142 setLenient(oldLenient);
2143 }
2144 }
2145 return;
2146
2147 case UCAL_WEEK_OF_YEAR:
2148 case UCAL_WEEK_OF_MONTH:
2149 case UCAL_DAY_OF_WEEK_IN_MONTH:
2150 delta *= kOneWeek;
2151 break;
2152
2153 case UCAL_AM_PM:
2154 delta *= 12 * kOneHour;
2155 break;
2156
2157 case UCAL_DAY_OF_MONTH:
2158 case UCAL_DAY_OF_YEAR:
2159 case UCAL_DAY_OF_WEEK:
2160 case UCAL_DOW_LOCAL:
2161 case UCAL_JULIAN_DAY:
2162 delta *= kOneDay;
2163 break;
2164
2165 case UCAL_HOUR_OF_DAY:
2166 case UCAL_HOUR:
2167 delta *= kOneHour;
2168 keepWallTimeInvariant = FALSE;
2169 break;
2170
2171 case UCAL_MINUTE:
2172 delta *= kOneMinute;
2173 keepWallTimeInvariant = FALSE;
2174 break;
2175
2176 case UCAL_SECOND:
2177 delta *= kOneSecond;
2178 keepWallTimeInvariant = FALSE;
2179 break;
2180
2181 case UCAL_MILLISECOND:
2182 case UCAL_MILLISECONDS_IN_DAY:
2183 keepWallTimeInvariant = FALSE;
2184 break;
2185
2186 default:
2187 #if defined (U_DEBUG_CAL)
2188 fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s not addable",
2189 __FILE__, __LINE__, fldName(field));
2190 #endif
2191 status = U_ILLEGAL_ARGUMENT_ERROR;
2192 return;
2193 // throw new IllegalArgumentException("Calendar.add(" + fieldName(field) +
2194 // ") not supported");
2195 }
2196
2197 // In order to keep the wall time invariant (for fields where this is
2198 // appropriate), check the combined DST & ZONE offset before and
2199 // after the add() operation. If it changes, then adjust the millis
2200 // to compensate.
2201 int32_t prevOffset = 0;
2202 int32_t prevWallTime = 0;
2203 if (keepWallTimeInvariant) {
2204 prevOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status);
2205 prevWallTime = get(UCAL_MILLISECONDS_IN_DAY, status);
2206 }
2207
2208 setTimeInMillis(getTimeInMillis(status) + delta, status);
2209
2210 if (keepWallTimeInvariant) {
2211 int32_t newWallTime = get(UCAL_MILLISECONDS_IN_DAY, status);
2212 if (newWallTime != prevWallTime) {
2213 // There is at least one zone transition between the base
2214 // time and the result time. As the result, wall time has
2215 // changed.
2216 UDate t = internalGetTime();
2217 int32_t newOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status);
2218 if (newOffset != prevOffset) {
2219 // When the difference of the previous UTC offset and
2220 // the new UTC offset exceeds 1 full day, we do not want
2221 // to roll over/back the date. For now, this only happens
2222 // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452.
2223 int32_t adjAmount = prevOffset - newOffset;
2224 adjAmount = adjAmount >= 0 ? adjAmount % (int32_t)kOneDay : -(-adjAmount % (int32_t)kOneDay);
2225 if (adjAmount != 0) {
2226 setTimeInMillis(t + adjAmount, status);
2227 newWallTime = get(UCAL_MILLISECONDS_IN_DAY, status);
2228 }
2229 if (newWallTime != prevWallTime) {
2230 // The result wall time or adjusted wall time was shifted because
2231 // the target wall time does not exist on the result date.
2232 switch (fSkippedWallTime) {
2233 case UCAL_WALLTIME_FIRST:
2234 if (adjAmount > 0) {
2235 setTimeInMillis(t, status);
2236 }
2237 break;
2238 case UCAL_WALLTIME_LAST:
2239 if (adjAmount < 0) {
2240 setTimeInMillis(t, status);
2241 }
2242 break;
2243 case UCAL_WALLTIME_NEXT_VALID:
2244 UDate tmpT = adjAmount > 0 ? internalGetTime() : t;
2245 UDate immediatePrevTrans;
2246 UBool hasTransition = getImmediatePreviousZoneTransition(tmpT, &immediatePrevTrans, status);
2247 if (U_SUCCESS(status) && hasTransition) {
2248 setTimeInMillis(immediatePrevTrans, status);
2249 }
2250 break;
2251 }
2252 }
2253 }
2254 }
2255 }
2256 }
2257
2258 // -------------------------------------
fieldDifference(UDate when,EDateFields field,UErrorCode & status)2259 int32_t Calendar::fieldDifference(UDate when, EDateFields field, UErrorCode& status) {
2260 return fieldDifference(when, (UCalendarDateFields) field, status);
2261 }
2262
fieldDifference(UDate targetMs,UCalendarDateFields field,UErrorCode & ec)2263 int32_t Calendar::fieldDifference(UDate targetMs, UCalendarDateFields field, UErrorCode& ec) {
2264 if (U_FAILURE(ec)) return 0;
2265 int32_t min = 0;
2266 double startMs = getTimeInMillis(ec);
2267 // Always add from the start millis. This accomodates
2268 // operations like adding years from February 29, 2000 up to
2269 // February 29, 2004. If 1, 1, 1, 1 is added to the year
2270 // field, the DOM gets pinned to 28 and stays there, giving an
2271 // incorrect DOM difference of 1. We have to add 1, reset, 2,
2272 // reset, 3, reset, 4.
2273 if (startMs < targetMs) {
2274 int32_t max = 1;
2275 // Find a value that is too large
2276 while (U_SUCCESS(ec)) {
2277 setTimeInMillis(startMs, ec);
2278 add(field, max, ec);
2279 double ms = getTimeInMillis(ec);
2280 if (ms == targetMs) {
2281 return max;
2282 } else if (ms > targetMs) {
2283 break;
2284 } else if (max < INT32_MAX) {
2285 min = max;
2286 max <<= 1;
2287 if (max < 0) {
2288 max = INT32_MAX;
2289 }
2290 } else {
2291 // Field difference too large to fit into int32_t
2292 #if defined (U_DEBUG_CAL)
2293 fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
2294 __FILE__, __LINE__, fldName(field));
2295 #endif
2296 ec = U_ILLEGAL_ARGUMENT_ERROR;
2297 }
2298 }
2299 // Do a binary search
2300 while ((max - min) > 1 && U_SUCCESS(ec)) {
2301 int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX
2302 setTimeInMillis(startMs, ec);
2303 add(field, t, ec);
2304 double ms = getTimeInMillis(ec);
2305 if (ms == targetMs) {
2306 return t;
2307 } else if (ms > targetMs) {
2308 max = t;
2309 } else {
2310 min = t;
2311 }
2312 }
2313 } else if (startMs > targetMs) {
2314 int32_t max = -1;
2315 // Find a value that is too small
2316 while (U_SUCCESS(ec)) {
2317 setTimeInMillis(startMs, ec);
2318 add(field, max, ec);
2319 double ms = getTimeInMillis(ec);
2320 if (ms == targetMs) {
2321 return max;
2322 } else if (ms < targetMs) {
2323 break;
2324 } else {
2325 min = max;
2326 max <<= 1;
2327 if (max == 0) {
2328 // Field difference too large to fit into int32_t
2329 #if defined (U_DEBUG_CAL)
2330 fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
2331 __FILE__, __LINE__, fldName(field));
2332 #endif
2333 ec = U_ILLEGAL_ARGUMENT_ERROR;
2334 }
2335 }
2336 }
2337 // Do a binary search
2338 while ((min - max) > 1 && U_SUCCESS(ec)) {
2339 int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX
2340 setTimeInMillis(startMs, ec);
2341 add(field, t, ec);
2342 double ms = getTimeInMillis(ec);
2343 if (ms == targetMs) {
2344 return t;
2345 } else if (ms < targetMs) {
2346 max = t;
2347 } else {
2348 min = t;
2349 }
2350 }
2351 }
2352 // Set calendar to end point
2353 setTimeInMillis(startMs, ec);
2354 add(field, min, ec);
2355
2356 /* Test for buffer overflows */
2357 if(U_FAILURE(ec)) {
2358 return 0;
2359 }
2360 return min;
2361 }
2362
2363 // -------------------------------------
2364
2365 void
adoptTimeZone(TimeZone * zone)2366 Calendar::adoptTimeZone(TimeZone* zone)
2367 {
2368 // Do nothing if passed-in zone is NULL
2369 if (zone == NULL) return;
2370
2371 // fZone should always be non-null
2372 delete fZone;
2373 fZone = zone;
2374
2375 // if the zone changes, we need to recompute the time fields
2376 fAreFieldsSet = FALSE;
2377 }
2378
2379 // -------------------------------------
2380 void
setTimeZone(const TimeZone & zone)2381 Calendar::setTimeZone(const TimeZone& zone)
2382 {
2383 adoptTimeZone(zone.clone());
2384 }
2385
2386 // -------------------------------------
2387
2388 const TimeZone&
getTimeZone() const2389 Calendar::getTimeZone() const
2390 {
2391 U_ASSERT(fZone != NULL);
2392 return *fZone;
2393 }
2394
2395 // -------------------------------------
2396
2397 TimeZone*
orphanTimeZone()2398 Calendar::orphanTimeZone()
2399 {
2400 // we let go of the time zone; the new time zone is the system default time zone
2401 TimeZone *defaultZone = TimeZone::createDefault();
2402 if (defaultZone == NULL) {
2403 // No error handling available. Must keep fZone non-NULL, there are many unchecked uses.
2404 return NULL;
2405 }
2406 TimeZone *z = fZone;
2407 fZone = defaultZone;
2408 return z;
2409 }
2410
2411 // -------------------------------------
2412
2413 void
setLenient(UBool lenient)2414 Calendar::setLenient(UBool lenient)
2415 {
2416 fLenient = lenient;
2417 }
2418
2419 // -------------------------------------
2420
2421 UBool
isLenient() const2422 Calendar::isLenient() const
2423 {
2424 return fLenient;
2425 }
2426
2427 // -------------------------------------
2428
2429 void
setRepeatedWallTimeOption(UCalendarWallTimeOption option)2430 Calendar::setRepeatedWallTimeOption(UCalendarWallTimeOption option)
2431 {
2432 if (option == UCAL_WALLTIME_LAST || option == UCAL_WALLTIME_FIRST) {
2433 fRepeatedWallTime = option;
2434 }
2435 }
2436
2437 // -------------------------------------
2438
2439 UCalendarWallTimeOption
getRepeatedWallTimeOption(void) const2440 Calendar::getRepeatedWallTimeOption(void) const
2441 {
2442 return fRepeatedWallTime;
2443 }
2444
2445 // -------------------------------------
2446
2447 void
setSkippedWallTimeOption(UCalendarWallTimeOption option)2448 Calendar::setSkippedWallTimeOption(UCalendarWallTimeOption option)
2449 {
2450 fSkippedWallTime = option;
2451 }
2452
2453 // -------------------------------------
2454
2455 UCalendarWallTimeOption
getSkippedWallTimeOption(void) const2456 Calendar::getSkippedWallTimeOption(void) const
2457 {
2458 return fSkippedWallTime;
2459 }
2460
2461 // -------------------------------------
2462
2463 void
setFirstDayOfWeek(UCalendarDaysOfWeek value)2464 Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value)
2465 {
2466 if (fFirstDayOfWeek != value &&
2467 value >= UCAL_SUNDAY && value <= UCAL_SATURDAY) {
2468 fFirstDayOfWeek = value;
2469 fAreFieldsSet = FALSE;
2470 }
2471 }
2472
2473 // -------------------------------------
2474
2475 Calendar::EDaysOfWeek
getFirstDayOfWeek() const2476 Calendar::getFirstDayOfWeek() const
2477 {
2478 return (Calendar::EDaysOfWeek)fFirstDayOfWeek;
2479 }
2480
2481 UCalendarDaysOfWeek
getFirstDayOfWeek(UErrorCode &) const2482 Calendar::getFirstDayOfWeek(UErrorCode & /*status*/) const
2483 {
2484 return fFirstDayOfWeek;
2485 }
2486 // -------------------------------------
2487
2488 void
setMinimalDaysInFirstWeek(uint8_t value)2489 Calendar::setMinimalDaysInFirstWeek(uint8_t value)
2490 {
2491 // Values less than 1 have the same effect as 1; values greater
2492 // than 7 have the same effect as 7. However, we normalize values
2493 // so operator== and so forth work.
2494 if (value < 1) {
2495 value = 1;
2496 } else if (value > 7) {
2497 value = 7;
2498 }
2499 if (fMinimalDaysInFirstWeek != value) {
2500 fMinimalDaysInFirstWeek = value;
2501 fAreFieldsSet = FALSE;
2502 }
2503 }
2504
2505 // -------------------------------------
2506
2507 uint8_t
getMinimalDaysInFirstWeek() const2508 Calendar::getMinimalDaysInFirstWeek() const
2509 {
2510 return fMinimalDaysInFirstWeek;
2511 }
2512
2513 // -------------------------------------
2514 // weekend functions, just dummy implementations for now (for API freeze)
2515
2516 UCalendarWeekdayType
getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek,UErrorCode & status) const2517 Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const
2518 {
2519 if (U_FAILURE(status)) {
2520 return UCAL_WEEKDAY;
2521 }
2522 if (dayOfWeek < UCAL_SUNDAY || dayOfWeek > UCAL_SATURDAY) {
2523 status = U_ILLEGAL_ARGUMENT_ERROR;
2524 return UCAL_WEEKDAY;
2525 }
2526 if (fWeekendOnset == fWeekendCease) {
2527 if (dayOfWeek != fWeekendOnset)
2528 return UCAL_WEEKDAY;
2529 return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET;
2530 }
2531 if (fWeekendOnset < fWeekendCease) {
2532 if (dayOfWeek < fWeekendOnset || dayOfWeek > fWeekendCease) {
2533 return UCAL_WEEKDAY;
2534 }
2535 } else {
2536 if (dayOfWeek > fWeekendCease && dayOfWeek < fWeekendOnset) {
2537 return UCAL_WEEKDAY;
2538 }
2539 }
2540 if (dayOfWeek == fWeekendOnset) {
2541 return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET;
2542 }
2543 if (dayOfWeek == fWeekendCease) {
2544 return (fWeekendCeaseMillis >= 86400000) ? UCAL_WEEKEND : UCAL_WEEKEND_CEASE;
2545 }
2546 return UCAL_WEEKEND;
2547 }
2548
2549 int32_t
getWeekendTransition(UCalendarDaysOfWeek dayOfWeek,UErrorCode & status) const2550 Calendar::getWeekendTransition(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const
2551 {
2552 if (U_FAILURE(status)) {
2553 return 0;
2554 }
2555 if (dayOfWeek == fWeekendOnset) {
2556 return fWeekendOnsetMillis;
2557 } else if (dayOfWeek == fWeekendCease) {
2558 return fWeekendCeaseMillis;
2559 }
2560 status = U_ILLEGAL_ARGUMENT_ERROR;
2561 return 0;
2562 }
2563
2564 UBool
isWeekend(UDate date,UErrorCode & status) const2565 Calendar::isWeekend(UDate date, UErrorCode &status) const
2566 {
2567 if (U_FAILURE(status)) {
2568 return FALSE;
2569 }
2570 // clone the calendar so we don't mess with the real one.
2571 Calendar *work = (Calendar*)this->clone();
2572 if (work == NULL) {
2573 status = U_MEMORY_ALLOCATION_ERROR;
2574 return FALSE;
2575 }
2576 UBool result = FALSE;
2577 work->setTime(date, status);
2578 if (U_SUCCESS(status)) {
2579 result = work->isWeekend();
2580 }
2581 delete work;
2582 return result;
2583 }
2584
2585 UBool
isWeekend(void) const2586 Calendar::isWeekend(void) const
2587 {
2588 UErrorCode status = U_ZERO_ERROR;
2589 UCalendarDaysOfWeek dayOfWeek = (UCalendarDaysOfWeek)get(UCAL_DAY_OF_WEEK, status);
2590 UCalendarWeekdayType dayType = getDayOfWeekType(dayOfWeek, status);
2591 if (U_SUCCESS(status)) {
2592 switch (dayType) {
2593 case UCAL_WEEKDAY:
2594 return FALSE;
2595 case UCAL_WEEKEND:
2596 return TRUE;
2597 case UCAL_WEEKEND_ONSET:
2598 case UCAL_WEEKEND_CEASE:
2599 // Use internalGet() because the above call to get() populated all fields.
2600 {
2601 int32_t millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY);
2602 int32_t transitionMillis = getWeekendTransition(dayOfWeek, status);
2603 if (U_SUCCESS(status)) {
2604 return (dayType == UCAL_WEEKEND_ONSET)?
2605 (millisInDay >= transitionMillis):
2606 (millisInDay < transitionMillis);
2607 }
2608 // else fall through, return FALSE
2609 U_FALLTHROUGH;
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 U_FALLTHROUGH;
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 // Get the monthNames resource bundle for the calendar 'type'. Fallback to gregorian if the resource is not
3799 // found.
3800 LocalUResourceBundlePointer calData(ures_open(NULL, useLocale.getBaseName(), &status));
3801 ures_getByKey(calData.getAlias(), gCalendar, calData.getAlias(), &status);
3802
3803 LocalUResourceBundlePointer monthNames;
3804 if (type != NULL && *type != '\0' && uprv_strcmp(type, gGregorian) != 0) {
3805 monthNames.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), type, NULL, &status));
3806 ures_getByKeyWithFallback(monthNames.getAlias(), gMonthNames,
3807 monthNames.getAlias(), &status);
3808 }
3809
3810 if (monthNames.isNull() || status == U_MISSING_RESOURCE_ERROR) {
3811 status = U_ZERO_ERROR;
3812 monthNames.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), gGregorian,
3813 monthNames.orphan(), &status));
3814 ures_getByKeyWithFallback(monthNames.getAlias(), gMonthNames,
3815 monthNames.getAlias(), &status);
3816 }
3817
3818 if (U_SUCCESS(status)) {
3819 U_LOCALE_BASED(locBased,*this);
3820 locBased.setLocaleIDs(ures_getLocaleByType(monthNames.getAlias(), ULOC_VALID_LOCALE, &status),
3821 ures_getLocaleByType(monthNames.getAlias(), ULOC_ACTUAL_LOCALE, &status));
3822 } else {
3823 status = U_USING_FALLBACK_WARNING;
3824 return;
3825 }
3826
3827 char region[ULOC_COUNTRY_CAPACITY];
3828 (void)ulocimp_getRegionForSupplementalData(desiredLocale.getName(), TRUE, region, sizeof(region), &status);
3829
3830 // Read week data values from supplementalData week data
3831 UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", &status);
3832 ures_getByKey(rb, "weekData", rb, &status);
3833 UResourceBundle *weekData = ures_getByKey(rb, region, NULL, &status);
3834 if (status == U_MISSING_RESOURCE_ERROR && rb != NULL) {
3835 status = U_ZERO_ERROR;
3836 weekData = ures_getByKey(rb, "001", NULL, &status);
3837 }
3838
3839 if (U_FAILURE(status)) {
3840 status = U_USING_FALLBACK_WARNING;
3841 } else {
3842 int32_t arrLen;
3843 const int32_t *weekDataArr = ures_getIntVector(weekData,&arrLen,&status);
3844 if( U_SUCCESS(status) && arrLen == 6
3845 && 1 <= weekDataArr[0] && weekDataArr[0] <= 7
3846 && 1 <= weekDataArr[1] && weekDataArr[1] <= 7
3847 && 1 <= weekDataArr[2] && weekDataArr[2] <= 7
3848 && 1 <= weekDataArr[4] && weekDataArr[4] <= 7) {
3849 fFirstDayOfWeek = (UCalendarDaysOfWeek)weekDataArr[0];
3850 fMinimalDaysInFirstWeek = (uint8_t)weekDataArr[1];
3851 fWeekendOnset = (UCalendarDaysOfWeek)weekDataArr[2];
3852 fWeekendOnsetMillis = weekDataArr[3];
3853 fWeekendCease = (UCalendarDaysOfWeek)weekDataArr[4];
3854 fWeekendCeaseMillis = weekDataArr[5];
3855 } else {
3856 status = U_INVALID_FORMAT_ERROR;
3857 }
3858 }
3859 ures_close(weekData);
3860 ures_close(rb);
3861 }
3862
3863 /**
3864 * Recompute the time and update the status fields isTimeSet
3865 * and areFieldsSet. Callers should check isTimeSet and only
3866 * call this method if isTimeSet is false.
3867 */
3868 void
updateTime(UErrorCode & status)3869 Calendar::updateTime(UErrorCode& status)
3870 {
3871 computeTime(status);
3872 if(U_FAILURE(status))
3873 return;
3874
3875 // If we are lenient, we need to recompute the fields to normalize
3876 // the values. Also, if we haven't set all the fields yet (i.e.,
3877 // in a newly-created object), we need to fill in the fields. [LIU]
3878 if (isLenient() || ! fAreAllFieldsSet)
3879 fAreFieldsSet = FALSE;
3880
3881 fIsTimeSet = TRUE;
3882 fAreFieldsVirtuallySet = FALSE;
3883 }
3884
3885 Locale
getLocale(ULocDataLocaleType type,UErrorCode & status) const3886 Calendar::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
3887 U_LOCALE_BASED(locBased, *this);
3888 return locBased.getLocale(type, status);
3889 }
3890
3891 const char *
getLocaleID(ULocDataLocaleType type,UErrorCode & status) const3892 Calendar::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const {
3893 U_LOCALE_BASED(locBased, *this);
3894 return locBased.getLocaleID(type, status);
3895 }
3896
3897 void
recalculateStamp()3898 Calendar::recalculateStamp() {
3899 int32_t index;
3900 int32_t currentValue;
3901 int32_t j, i;
3902
3903 fNextStamp = 1;
3904
3905 for (j = 0; j < UCAL_FIELD_COUNT; j++) {
3906 currentValue = STAMP_MAX;
3907 index = -1;
3908 for (i = 0; i < UCAL_FIELD_COUNT; i++) {
3909 if (fStamp[i] > fNextStamp && fStamp[i] < currentValue) {
3910 currentValue = fStamp[i];
3911 index = i;
3912 }
3913 }
3914
3915 if (index >= 0) {
3916 fStamp[index] = ++fNextStamp;
3917 } else {
3918 break;
3919 }
3920 }
3921 fNextStamp++;
3922 }
3923
3924 // Deprecated function. This doesn't need to be inline.
3925 void
internalSet(EDateFields field,int32_t value)3926 Calendar::internalSet(EDateFields field, int32_t value)
3927 {
3928 internalSet((UCalendarDateFields) field, value);
3929 }
3930
3931 BasicTimeZone*
getBasicTimeZone(void) const3932 Calendar::getBasicTimeZone(void) const {
3933 if (dynamic_cast<const OlsonTimeZone *>(fZone) != NULL
3934 || dynamic_cast<const SimpleTimeZone *>(fZone) != NULL
3935 || dynamic_cast<const RuleBasedTimeZone *>(fZone) != NULL
3936 || dynamic_cast<const VTimeZone *>(fZone) != NULL) {
3937 return (BasicTimeZone*)fZone;
3938 }
3939 return NULL;
3940 }
3941
3942 U_NAMESPACE_END
3943
3944 #endif /* #if !UCONFIG_NO_FORMATTING */
3945
3946
3947 //eof
3948
3949