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