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