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