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