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