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