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