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