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