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