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