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