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