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