1/*
2*******************************************************************************
3* Copyright (C) 1997-2013, 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
1168void
1169Calendar::clear()
1170{
1171    for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
1172        fFields[i]     = 0; // Must do this; other code depends on it
1173        fStamp[i]     = kUnset;
1174        fIsSet[i]     = FALSE; // Remove later
1175    }
1176    fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE;
1177    // fTime is not 'cleared' - may be used if no fields are set.
1178}
1179
1180// -------------------------------------
1181
1182void
1183Calendar::clear(UCalendarDateFields field)
1184{
1185    if (fAreFieldsVirtuallySet) {
1186        UErrorCode ec = U_ZERO_ERROR;
1187        computeFields(ec);
1188    }
1189    fFields[field]         = 0;
1190    fStamp[field]         = kUnset;
1191    fIsSet[field]         = FALSE; // Remove later
1192    fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE;
1193}
1194
1195// -------------------------------------
1196
1197UBool
1198Calendar::isSet(UCalendarDateFields field) const
1199{
1200    return fAreFieldsVirtuallySet || (fStamp[field] != kUnset);
1201}
1202
1203
1204int32_t Calendar::newestStamp(UCalendarDateFields first, UCalendarDateFields last, int32_t bestStampSoFar) const
1205{
1206    int32_t bestStamp = bestStampSoFar;
1207    for (int32_t i=(int32_t)first; i<=(int32_t)last; ++i) {
1208        if (fStamp[i] > bestStamp) {
1209            bestStamp = fStamp[i];
1210        }
1211    }
1212    return bestStamp;
1213}
1214
1215
1216// -------------------------------------
1217
1218void
1219Calendar::complete(UErrorCode& status)
1220{
1221    if (!fIsTimeSet) {
1222        updateTime(status);
1223        /* Test for buffer overflows */
1224        if(U_FAILURE(status)) {
1225            return;
1226        }
1227    }
1228    if (!fAreFieldsSet) {
1229        computeFields(status); // fills in unset fields
1230        /* Test for buffer overflows */
1231        if(U_FAILURE(status)) {
1232            return;
1233        }
1234        fAreFieldsSet         = TRUE;
1235        fAreAllFieldsSet     = TRUE;
1236    }
1237}
1238
1239//-------------------------------------------------------------------------
1240// Protected utility methods for use by subclasses.  These are very handy
1241// for implementing add, roll, and computeFields.
1242//-------------------------------------------------------------------------
1243
1244/**
1245* Adjust the specified field so that it is within
1246* the allowable range for the date to which this calendar is set.
1247* For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH}
1248* field for a calendar set to April 31 would cause it to be set
1249* to April 30.
1250* <p>
1251* <b>Subclassing:</b>
1252* <br>
1253* This utility method is intended for use by subclasses that need to implement
1254* their own overrides of {@link #roll roll} and {@link #add add}.
1255* <p>
1256* <b>Note:</b>
1257* <code>pinField</code> is implemented in terms of
1258* {@link #getActualMinimum getActualMinimum}
1259* and {@link #getActualMaximum getActualMaximum}.  If either of those methods uses
1260* a slow, iterative algorithm for a particular field, it would be
1261* unwise to attempt to call <code>pinField</code> for that field.  If you
1262* really do need to do so, you should override this method to do
1263* something more efficient for that field.
1264* <p>
1265* @param field The calendar field whose value should be pinned.
1266*
1267* @see #getActualMinimum
1268* @see #getActualMaximum
1269* @stable ICU 2.0
1270*/
1271void Calendar::pinField(UCalendarDateFields field, UErrorCode& status) {
1272    int32_t max = getActualMaximum(field, status);
1273    int32_t min = getActualMinimum(field, status);
1274
1275    if (fFields[field] > max) {
1276        set(field, max);
1277    } else if (fFields[field] < min) {
1278        set(field, min);
1279    }
1280}
1281
1282
1283void Calendar::computeFields(UErrorCode &ec)
1284{
1285  if (U_FAILURE(ec)) {
1286        return;
1287    }
1288    // Compute local wall millis
1289    double localMillis = internalGetTime();
1290    int32_t rawOffset, dstOffset;
1291    getTimeZone().getOffset(localMillis, FALSE, rawOffset, dstOffset, ec);
1292    localMillis += (rawOffset + dstOffset);
1293
1294    // Mark fields as set.  Do this before calling handleComputeFields().
1295    uint32_t mask =   //fInternalSetMask;
1296        (1 << UCAL_ERA) |
1297        (1 << UCAL_YEAR) |
1298        (1 << UCAL_MONTH) |
1299        (1 << UCAL_DAY_OF_MONTH) | // = UCAL_DATE
1300        (1 << UCAL_DAY_OF_YEAR) |
1301        (1 << UCAL_EXTENDED_YEAR);
1302
1303    for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
1304        if ((mask & 1) == 0) {
1305            fStamp[i] = kInternallySet;
1306            fIsSet[i] = TRUE; // Remove later
1307        } else {
1308            fStamp[i] = kUnset;
1309            fIsSet[i] = FALSE; // Remove later
1310        }
1311        mask >>= 1;
1312    }
1313
1314    // We used to check for and correct extreme millis values (near
1315    // Long.MIN_VALUE or Long.MAX_VALUE) here.  Such values would cause
1316    // overflows from positive to negative (or vice versa) and had to
1317    // be manually tweaked.  We no longer need to do this because we
1318    // have limited the range of supported dates to those that have a
1319    // Julian day that fits into an int.  This allows us to implement a
1320    // JULIAN_DAY field and also removes some inelegant code. - Liu
1321    // 11/6/00
1322
1323    int32_t days =  (int32_t)ClockMath::floorDivide(localMillis, (double)kOneDay);
1324
1325    internalSet(UCAL_JULIAN_DAY,days + kEpochStartAsJulianDay);
1326
1327#if defined (U_DEBUG_CAL)
1328    //fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n",
1329    //__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
1330#endif
1331
1332    computeGregorianAndDOWFields(fFields[UCAL_JULIAN_DAY], ec);
1333
1334    // Call framework method to have subclass compute its fields.
1335    // These must include, at a minimum, MONTH, DAY_OF_MONTH,
1336    // EXTENDED_YEAR, YEAR, DAY_OF_YEAR.  This method will call internalSet(),
1337    // which will update stamp[].
1338    handleComputeFields(fFields[UCAL_JULIAN_DAY], ec);
1339
1340    // Compute week-related fields, based on the subclass-computed
1341    // fields computed by handleComputeFields().
1342    computeWeekFields(ec);
1343
1344    // Compute time-related fields.  These are indepent of the date and
1345    // of the subclass algorithm.  They depend only on the local zone
1346    // wall milliseconds in day.
1347    int32_t millisInDay =  (int32_t) (localMillis - (days * kOneDay));
1348    fFields[UCAL_MILLISECONDS_IN_DAY] = millisInDay;
1349    fFields[UCAL_MILLISECOND] = millisInDay % 1000;
1350    millisInDay /= 1000;
1351    fFields[UCAL_SECOND] = millisInDay % 60;
1352    millisInDay /= 60;
1353    fFields[UCAL_MINUTE] = millisInDay % 60;
1354    millisInDay /= 60;
1355    fFields[UCAL_HOUR_OF_DAY] = millisInDay;
1356    fFields[UCAL_AM_PM] = millisInDay / 12; // Assume AM == 0
1357    fFields[UCAL_HOUR] = millisInDay % 12;
1358    fFields[UCAL_ZONE_OFFSET] = rawOffset;
1359    fFields[UCAL_DST_OFFSET] = dstOffset;
1360}
1361
1362uint8_t Calendar::julianDayToDayOfWeek(double julian)
1363{
1364    // If julian is negative, then julian%7 will be negative, so we adjust
1365    // accordingly.  We add 1 because Julian day 0 is Monday.
1366    int8_t dayOfWeek = (int8_t) uprv_fmod(julian + 1, 7);
1367
1368    uint8_t result = (uint8_t)(dayOfWeek + ((dayOfWeek < 0) ? (7+UCAL_SUNDAY ) : UCAL_SUNDAY));
1369    return result;
1370}
1371
1372/**
1373* Compute the Gregorian calendar year, month, and day of month from
1374* the given Julian day.  These values are not stored in fields, but in
1375* member variables gregorianXxx.  Also compute the DAY_OF_WEEK and
1376* DOW_LOCAL fields.
1377*/
1378void Calendar::computeGregorianAndDOWFields(int32_t julianDay, UErrorCode &ec)
1379{
1380    computeGregorianFields(julianDay, ec);
1381
1382    // Compute day of week: JD 0 = Monday
1383    int32_t dow = julianDayToDayOfWeek(julianDay);
1384    internalSet(UCAL_DAY_OF_WEEK,dow);
1385
1386    // Calculate 1-based localized day of week
1387    int32_t dowLocal = dow - getFirstDayOfWeek() + 1;
1388    if (dowLocal < 1) {
1389        dowLocal += 7;
1390    }
1391    internalSet(UCAL_DOW_LOCAL,dowLocal);
1392    fFields[UCAL_DOW_LOCAL] = dowLocal;
1393}
1394
1395/**
1396* Compute the Gregorian calendar year, month, and day of month from the
1397* Julian day.  These values are not stored in fields, but in member
1398* variables gregorianXxx.  They are used for time zone computations and by
1399* subclasses that are Gregorian derivatives.  Subclasses may call this
1400* method to perform a Gregorian calendar millis->fields computation.
1401*/
1402void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode & /* ec */) {
1403    int32_t gregorianDayOfWeekUnused;
1404    Grego::dayToFields(julianDay - kEpochStartAsJulianDay, fGregorianYear, fGregorianMonth, fGregorianDayOfMonth, gregorianDayOfWeekUnused, fGregorianDayOfYear);
1405}
1406
1407/**
1408* Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH,
1409* DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR,
1410* DAY_OF_WEEK, and DAY_OF_YEAR.  The latter fields are computed by the
1411* subclass based on the calendar system.
1412*
1413* <p>The YEAR_WOY field is computed simplistically.  It is equal to YEAR
1414* most of the time, but at the year boundary it may be adjusted to YEAR-1
1415* or YEAR+1 to reflect the overlap of a week into an adjacent year.  In
1416* this case, a simple increment or decrement is performed on YEAR, even
1417* though this may yield an invalid YEAR value.  For instance, if the YEAR
1418* is part of a calendar system with an N-year cycle field CYCLE, then
1419* incrementing the YEAR may involve incrementing CYCLE and setting YEAR
1420* back to 0 or 1.  This is not handled by this code, and in fact cannot be
1421* simply handled without having subclasses define an entire parallel set of
1422* fields for fields larger than or equal to a year.  This additional
1423* complexity is not warranted, since the intention of the YEAR_WOY field is
1424* to support ISO 8601 notation, so it will typically be used with a
1425* proleptic Gregorian calendar, which has no field larger than a year.
1426*/
1427void Calendar::computeWeekFields(UErrorCode &ec) {
1428    if(U_FAILURE(ec)) {
1429        return;
1430    }
1431    int32_t eyear = fFields[UCAL_EXTENDED_YEAR];
1432    int32_t dayOfWeek = fFields[UCAL_DAY_OF_WEEK];
1433    int32_t dayOfYear = fFields[UCAL_DAY_OF_YEAR];
1434
1435    // WEEK_OF_YEAR start
1436    // Compute the week of the year.  For the Gregorian calendar, valid week
1437    // numbers run from 1 to 52 or 53, depending on the year, the first day
1438    // of the week, and the minimal days in the first week.  For other
1439    // calendars, the valid range may be different -- it depends on the year
1440    // length.  Days at the start of the year may fall into the last week of
1441    // the previous year; days at the end of the year may fall into the
1442    // first week of the next year.  ASSUME that the year length is less than
1443    // 7000 days.
1444    int32_t yearOfWeekOfYear = eyear;
1445    int32_t relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; // 0..6
1446    int32_t relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; // 0..6
1447    int32_t woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53
1448    if ((7 - relDowJan1) >= getMinimalDaysInFirstWeek()) {
1449        ++woy;
1450    }
1451
1452    // Adjust for weeks at the year end that overlap into the previous or
1453    // next calendar year.
1454    if (woy == 0) {
1455        // We are the last week of the previous year.
1456        // Check to see if we are in the last week; if so, we need
1457        // to handle the case in which we are the first week of the
1458        // next year.
1459
1460        int32_t prevDoy = dayOfYear + handleGetYearLength(eyear - 1);
1461        woy = weekNumber(prevDoy, dayOfWeek);
1462        yearOfWeekOfYear--;
1463    } else {
1464        int32_t lastDoy = handleGetYearLength(eyear);
1465        // Fast check: For it to be week 1 of the next year, the DOY
1466        // must be on or after L-5, where L is yearLength(), then it
1467        // cannot possibly be week 1 of the next year:
1468        //          L-5                  L
1469        // doy: 359 360 361 362 363 364 365 001
1470        // dow:      1   2   3   4   5   6   7
1471        if (dayOfYear >= (lastDoy - 5)) {
1472            int32_t lastRelDow = (relDow + lastDoy - dayOfYear) % 7;
1473            if (lastRelDow < 0) {
1474                lastRelDow += 7;
1475            }
1476            if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) &&
1477                ((dayOfYear + 7 - relDow) > lastDoy)) {
1478                    woy = 1;
1479                    yearOfWeekOfYear++;
1480                }
1481        }
1482    }
1483    fFields[UCAL_WEEK_OF_YEAR] = woy;
1484    fFields[UCAL_YEAR_WOY] = yearOfWeekOfYear;
1485    // WEEK_OF_YEAR end
1486
1487    int32_t dayOfMonth = fFields[UCAL_DAY_OF_MONTH];
1488    fFields[UCAL_WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek);
1489    fFields[UCAL_DAY_OF_WEEK_IN_MONTH] = (dayOfMonth-1) / 7 + 1;
1490#if defined (U_DEBUG_CAL)
1491    if(fFields[UCAL_DAY_OF_WEEK_IN_MONTH]==0) fprintf(stderr, "%s:%d: DOWIM %d on %g\n",
1492        __FILE__, __LINE__,fFields[UCAL_DAY_OF_WEEK_IN_MONTH], fTime);
1493#endif
1494}
1495
1496
1497int32_t Calendar::weekNumber(int32_t desiredDay, int32_t dayOfPeriod, int32_t dayOfWeek)
1498{
1499    // Determine the day of the week of the first day of the period
1500    // in question (either a year or a month).  Zero represents the
1501    // first day of the week on this calendar.
1502    int32_t periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % 7;
1503    if (periodStartDayOfWeek < 0) periodStartDayOfWeek += 7;
1504
1505    // Compute the week number.  Initially, ignore the first week, which
1506    // may be fractional (or may not be).  We add periodStartDayOfWeek in
1507    // order to fill out the first week, if it is fractional.
1508    int32_t weekNo = (desiredDay + periodStartDayOfWeek - 1)/7;
1509
1510    // If the first week is long enough, then count it.  If
1511    // the minimal days in the first week is one, or if the period start
1512    // is zero, we always increment weekNo.
1513    if ((7 - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo;
1514
1515    return weekNo;
1516}
1517
1518void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode &/* status */)
1519{
1520    internalSet(UCAL_MONTH, getGregorianMonth());
1521    internalSet(UCAL_DAY_OF_MONTH, getGregorianDayOfMonth());
1522    internalSet(UCAL_DAY_OF_YEAR, getGregorianDayOfYear());
1523    int32_t eyear = getGregorianYear();
1524    internalSet(UCAL_EXTENDED_YEAR, eyear);
1525    int32_t era = GregorianCalendar::AD;
1526    if (eyear < 1) {
1527        era = GregorianCalendar::BC;
1528        eyear = 1 - eyear;
1529    }
1530    internalSet(UCAL_ERA, era);
1531    internalSet(UCAL_YEAR, eyear);
1532}
1533// -------------------------------------
1534
1535
1536void Calendar::roll(EDateFields field, int32_t amount, UErrorCode& status)
1537{
1538    roll((UCalendarDateFields)field, amount, status);
1539}
1540
1541void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status)
1542{
1543    if (amount == 0) {
1544        return; // Nothing to do
1545    }
1546
1547    complete(status);
1548
1549    if(U_FAILURE(status)) {
1550        return;
1551    }
1552    switch (field) {
1553    case UCAL_DAY_OF_MONTH:
1554    case UCAL_AM_PM:
1555    case UCAL_MINUTE:
1556    case UCAL_SECOND:
1557    case UCAL_MILLISECOND:
1558    case UCAL_MILLISECONDS_IN_DAY:
1559    case UCAL_ERA:
1560        // These are the standard roll instructions.  These work for all
1561        // simple cases, that is, cases in which the limits are fixed, such
1562        // as the hour, the day of the month, and the era.
1563        {
1564            int32_t min = getActualMinimum(field,status);
1565            int32_t max = getActualMaximum(field,status);
1566            int32_t gap = max - min + 1;
1567
1568            int32_t value = internalGet(field) + amount;
1569            value = (value - min) % gap;
1570            if (value < 0) {
1571                value += gap;
1572            }
1573            value += min;
1574
1575            set(field, value);
1576            return;
1577        }
1578
1579    case UCAL_HOUR:
1580    case UCAL_HOUR_OF_DAY:
1581        // Rolling the hour is difficult on the ONSET and CEASE days of
1582        // daylight savings.  For example, if the change occurs at
1583        // 2 AM, we have the following progression:
1584        // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst
1585        // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std
1586        // To get around this problem we don't use fields; we manipulate
1587        // the time in millis directly.
1588        {
1589            // Assume min == 0 in calculations below
1590            double start = getTimeInMillis(status);
1591            int32_t oldHour = internalGet(field);
1592            int32_t max = getMaximum(field);
1593            int32_t newHour = (oldHour + amount) % (max + 1);
1594            if (newHour < 0) {
1595                newHour += max + 1;
1596            }
1597            setTimeInMillis(start + kOneHour * (newHour - oldHour),status);
1598            return;
1599        }
1600
1601    case UCAL_MONTH:
1602        // Rolling the month involves both pinning the final value
1603        // and adjusting the DAY_OF_MONTH if necessary.  We only adjust the
1604        // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
1605        // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
1606        {
1607            int32_t max = getActualMaximum(UCAL_MONTH, status);
1608            int32_t mon = (internalGet(UCAL_MONTH) + amount) % (max+1);
1609
1610            if (mon < 0) {
1611                mon += (max + 1);
1612            }
1613            set(UCAL_MONTH, mon);
1614
1615            // Keep the day of month in range.  We don't want to spill over
1616            // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 ->
1617            // mar3.
1618            pinField(UCAL_DAY_OF_MONTH,status);
1619            return;
1620        }
1621
1622    case UCAL_YEAR:
1623    case UCAL_YEAR_WOY:
1624        {
1625            // * If era==0 and years go backwards in time, change sign of amount.
1626            // * Until we have new API per #9393, we temporarily hardcode knowledge of
1627            //   which calendars have era 0 years that go backwards.
1628            UBool era0WithYearsThatGoBackwards = FALSE;
1629            int32_t era = get(UCAL_ERA, status);
1630            if (era == 0) {
1631                const char * calType = getType();
1632                if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) {
1633                    amount = -amount;
1634                    era0WithYearsThatGoBackwards = TRUE;
1635                }
1636            }
1637            int32_t newYear = internalGet(field) + amount;
1638            if (era > 0 || newYear >= 1) {
1639                int32_t maxYear = getActualMaximum(field, status);
1640                if (maxYear < 32768) {
1641                    // this era has real bounds, roll should wrap years
1642                    if (newYear < 1) {
1643                        newYear = maxYear - ((-newYear) % maxYear);
1644                    } else if (newYear > maxYear) {
1645                        newYear = ((newYear - 1) % maxYear) + 1;
1646                    }
1647                // else era is unbounded, just pin low year instead of wrapping
1648                } else if (newYear < 1) {
1649                    newYear = 1;
1650                }
1651            // else we are in era 0 with newYear < 1;
1652            // calendars with years that go backwards must pin the year value at 0,
1653            // other calendars can have years < 0 in era 0
1654            } else if (era0WithYearsThatGoBackwards) {
1655                newYear = 1;
1656            }
1657            set(field, newYear);
1658            pinField(UCAL_MONTH,status);
1659            pinField(UCAL_DAY_OF_MONTH,status);
1660            return;
1661        }
1662
1663    case UCAL_EXTENDED_YEAR:
1664        // Rolling the year can involve pinning the DAY_OF_MONTH.
1665        set(field, internalGet(field) + amount);
1666        pinField(UCAL_MONTH,status);
1667        pinField(UCAL_DAY_OF_MONTH,status);
1668        return;
1669
1670    case UCAL_WEEK_OF_MONTH:
1671        {
1672            // This is tricky, because during the roll we may have to shift
1673            // to a different day of the week.  For example:
1674
1675            //    s  m  t  w  r  f  s
1676            //          1  2  3  4  5
1677            //    6  7  8  9 10 11 12
1678
1679            // When rolling from the 6th or 7th back one week, we go to the
1680            // 1st (assuming that the first partial week counts).  The same
1681            // thing happens at the end of the month.
1682
1683            // The other tricky thing is that we have to figure out whether
1684            // the first partial week actually counts or not, based on the
1685            // minimal first days in the week.  And we have to use the
1686            // correct first day of the week to delineate the week
1687            // boundaries.
1688
1689            // Here's our algorithm.  First, we find the real boundaries of
1690            // the month.  Then we discard the first partial week if it
1691            // doesn't count in this locale.  Then we fill in the ends with
1692            // phantom days, so that the first partial week and the last
1693            // partial week are full weeks.  We then have a nice square
1694            // block of weeks.  We do the usual rolling within this block,
1695            // as is done elsewhere in this method.  If we wind up on one of
1696            // the phantom days that we added, we recognize this and pin to
1697            // the first or the last day of the month.  Easy, eh?
1698
1699            // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
1700            // in this locale.  We have dow in 0..6.
1701            int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
1702            if (dow < 0) dow += 7;
1703
1704            // Find the day of the week (normalized for locale) for the first
1705            // of the month.
1706            int32_t fdm = (dow - internalGet(UCAL_DAY_OF_MONTH) + 1) % 7;
1707            if (fdm < 0) fdm += 7;
1708
1709            // Get the first day of the first full week of the month,
1710            // including phantom days, if any.  Figure out if the first week
1711            // counts or not; if it counts, then fill in phantom days.  If
1712            // not, advance to the first real full week (skip the partial week).
1713            int32_t start;
1714            if ((7 - fdm) < getMinimalDaysInFirstWeek())
1715                start = 8 - fdm; // Skip the first partial week
1716            else
1717                start = 1 - fdm; // This may be zero or negative
1718
1719            // Get the day of the week (normalized for locale) for the last
1720            // day of the month.
1721            int32_t monthLen = getActualMaximum(UCAL_DAY_OF_MONTH, status);
1722            int32_t ldm = (monthLen - internalGet(UCAL_DAY_OF_MONTH) + dow) % 7;
1723            // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.
1724
1725            // Get the limit day for the blocked-off rectangular month; that
1726            // is, the day which is one past the last day of the month,
1727            // after the month has already been filled in with phantom days
1728            // to fill out the last week.  This day has a normalized DOW of 0.
1729            int32_t limit = monthLen + 7 - ldm;
1730
1731            // Now roll between start and (limit - 1).
1732            int32_t gap = limit - start;
1733            int32_t day_of_month = (internalGet(UCAL_DAY_OF_MONTH) + amount*7 -
1734                start) % gap;
1735            if (day_of_month < 0) day_of_month += gap;
1736            day_of_month += start;
1737
1738            // Finally, pin to the real start and end of the month.
1739            if (day_of_month < 1) day_of_month = 1;
1740            if (day_of_month > monthLen) day_of_month = monthLen;
1741
1742            // Set the DAY_OF_MONTH.  We rely on the fact that this field
1743            // takes precedence over everything else (since all other fields
1744            // are also set at this point).  If this fact changes (if the
1745            // disambiguation algorithm changes) then we will have to unset
1746            // the appropriate fields here so that DAY_OF_MONTH is attended
1747            // to.
1748            set(UCAL_DAY_OF_MONTH, day_of_month);
1749            return;
1750        }
1751    case UCAL_WEEK_OF_YEAR:
1752        {
1753            // This follows the outline of WEEK_OF_MONTH, except it applies
1754            // to the whole year.  Please see the comment for WEEK_OF_MONTH
1755            // for general notes.
1756
1757            // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
1758            // in this locale.  We have dow in 0..6.
1759            int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
1760            if (dow < 0) dow += 7;
1761
1762            // Find the day of the week (normalized for locale) for the first
1763            // of the year.
1764            int32_t fdy = (dow - internalGet(UCAL_DAY_OF_YEAR) + 1) % 7;
1765            if (fdy < 0) fdy += 7;
1766
1767            // Get the first day of the first full week of the year,
1768            // including phantom days, if any.  Figure out if the first week
1769            // counts or not; if it counts, then fill in phantom days.  If
1770            // not, advance to the first real full week (skip the partial week).
1771            int32_t start;
1772            if ((7 - fdy) < getMinimalDaysInFirstWeek())
1773                start = 8 - fdy; // Skip the first partial week
1774            else
1775                start = 1 - fdy; // This may be zero or negative
1776
1777            // Get the day of the week (normalized for locale) for the last
1778            // day of the year.
1779            int32_t yearLen = getActualMaximum(UCAL_DAY_OF_YEAR,status);
1780            int32_t ldy = (yearLen - internalGet(UCAL_DAY_OF_YEAR) + dow) % 7;
1781            // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here.
1782
1783            // Get the limit day for the blocked-off rectangular year; that
1784            // is, the day which is one past the last day of the year,
1785            // after the year has already been filled in with phantom days
1786            // to fill out the last week.  This day has a normalized DOW of 0.
1787            int32_t limit = yearLen + 7 - ldy;
1788
1789            // Now roll between start and (limit - 1).
1790            int32_t gap = limit - start;
1791            int32_t day_of_year = (internalGet(UCAL_DAY_OF_YEAR) + amount*7 -
1792                start) % gap;
1793            if (day_of_year < 0) day_of_year += gap;
1794            day_of_year += start;
1795
1796            // Finally, pin to the real start and end of the month.
1797            if (day_of_year < 1) day_of_year = 1;
1798            if (day_of_year > yearLen) day_of_year = yearLen;
1799
1800            // Make sure that the year and day of year are attended to by
1801            // clearing other fields which would normally take precedence.
1802            // If the disambiguation algorithm is changed, this section will
1803            // have to be updated as well.
1804            set(UCAL_DAY_OF_YEAR, day_of_year);
1805            clear(UCAL_MONTH);
1806            return;
1807        }
1808    case UCAL_DAY_OF_YEAR:
1809        {
1810            // Roll the day of year using millis.  Compute the millis for
1811            // the start of the year, and get the length of the year.
1812            double delta = amount * kOneDay; // Scale up from days to millis
1813            double min2 = internalGet(UCAL_DAY_OF_YEAR)-1;
1814            min2 *= kOneDay;
1815            min2 = internalGetTime() - min2;
1816
1817            //      double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay;
1818            double newtime;
1819
1820            double yearLength = getActualMaximum(UCAL_DAY_OF_YEAR,status);
1821            double oneYear = yearLength;
1822            oneYear *= kOneDay;
1823            newtime = uprv_fmod((internalGetTime() + delta - min2), oneYear);
1824            if (newtime < 0) newtime += oneYear;
1825            setTimeInMillis(newtime + min2, status);
1826            return;
1827        }
1828    case UCAL_DAY_OF_WEEK:
1829    case UCAL_DOW_LOCAL:
1830        {
1831            // Roll the day of week using millis.  Compute the millis for
1832            // the start of the week, using the first day of week setting.
1833            // Restrict the millis to [start, start+7days).
1834            double delta = amount * kOneDay; // Scale up from days to millis
1835            // Compute the number of days before the current day in this
1836            // week.  This will be a value 0..6.
1837            int32_t leadDays = internalGet(field);
1838            leadDays -= (field == UCAL_DAY_OF_WEEK) ? getFirstDayOfWeek() : 1;
1839            if (leadDays < 0) leadDays += 7;
1840            double min2 = internalGetTime() - leadDays * kOneDay;
1841            double newtime = uprv_fmod((internalGetTime() + delta - min2), kOneWeek);
1842            if (newtime < 0) newtime += kOneWeek;
1843            setTimeInMillis(newtime + min2, status);
1844            return;
1845        }
1846    case UCAL_DAY_OF_WEEK_IN_MONTH:
1847        {
1848            // Roll the day of week in the month using millis.  Determine
1849            // the first day of the week in the month, and then the last,
1850            // and then roll within that range.
1851            double delta = amount * kOneWeek; // Scale up from weeks to millis
1852            // Find the number of same days of the week before this one
1853            // in this month.
1854            int32_t preWeeks = (internalGet(UCAL_DAY_OF_MONTH) - 1) / 7;
1855            // Find the number of same days of the week after this one
1856            // in this month.
1857            int32_t postWeeks = (getActualMaximum(UCAL_DAY_OF_MONTH,status) -
1858                internalGet(UCAL_DAY_OF_MONTH)) / 7;
1859            // From these compute the min and gap millis for rolling.
1860            double min2 = internalGetTime() - preWeeks * kOneWeek;
1861            double gap2 = kOneWeek * (preWeeks + postWeeks + 1); // Must add 1!
1862            // Roll within this range
1863            double newtime = uprv_fmod((internalGetTime() + delta - min2), gap2);
1864            if (newtime < 0) newtime += gap2;
1865            setTimeInMillis(newtime + min2, status);
1866            return;
1867        }
1868    case UCAL_JULIAN_DAY:
1869        set(field, internalGet(field) + amount);
1870        return;
1871    default:
1872        // Other fields cannot be rolled by this method
1873#if defined (U_DEBUG_CAL)
1874        fprintf(stderr, "%s:%d: ILLEGAL ARG because of roll on non-rollable field %s\n",
1875            __FILE__, __LINE__,fldName(field));
1876#endif
1877        status = U_ILLEGAL_ARGUMENT_ERROR;
1878    }
1879}
1880
1881void Calendar::add(EDateFields field, int32_t amount, UErrorCode& status)
1882{
1883    Calendar::add((UCalendarDateFields)field, amount, status);
1884}
1885
1886// -------------------------------------
1887void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status)
1888{
1889    if (amount == 0) {
1890        return;   // Do nothing!
1891    }
1892
1893    // We handle most fields in the same way.  The algorithm is to add
1894    // a computed amount of millis to the current millis.  The only
1895    // wrinkle is with DST (and/or a change to the zone's UTC offset, which
1896    // we'll include with DST) -- for some fields, like the DAY_OF_MONTH,
1897    // we don't want the HOUR to shift due to changes in DST.  If the
1898    // result of the add operation is to move from DST to Standard, or
1899    // vice versa, we need to adjust by an hour forward or back,
1900    // respectively.  For such fields we set keepHourInvariant to TRUE.
1901
1902    // We only adjust the DST for fields larger than an hour.  For
1903    // fields smaller than an hour, we cannot adjust for DST without
1904    // causing problems.  for instance, if you add one hour to April 5,
1905    // 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an
1906    // illegal value), but then the adjustment sees the change and
1907    // compensates by subtracting an hour.  As a result the time
1908    // doesn't advance at all.
1909
1910    // For some fields larger than a day, such as a UCAL_MONTH, we pin the
1911    // UCAL_DAY_OF_MONTH.  This allows <March 31>.add(UCAL_MONTH, 1) to be
1912    // <April 30>, rather than <April 31> => <May 1>.
1913
1914    double delta = amount; // delta in ms
1915    UBool keepHourInvariant = TRUE;
1916
1917    switch (field) {
1918    case UCAL_ERA:
1919        set(field, get(field, status) + amount);
1920        pinField(UCAL_ERA, status);
1921        return;
1922
1923    case UCAL_YEAR:
1924    case UCAL_YEAR_WOY:
1925      {
1926        // * If era=0 and years go backwards in time, change sign of amount.
1927        // * Until we have new API per #9393, we temporarily hardcode knowledge of
1928        //   which calendars have era 0 years that go backwards.
1929        // * Note that for UCAL_YEAR (but not UCAL_YEAR_WOY) we could instead handle
1930        //   this by applying the amount to the UCAL_EXTENDED_YEAR field; but since
1931        //   we would still need to handle UCAL_YEAR_WOY as below, might as well
1932        //   also handle UCAL_YEAR the same way.
1933        int32_t era = get(UCAL_ERA, status);
1934        if (era == 0) {
1935          const char * calType = getType();
1936          if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) {
1937            amount = -amount;
1938          }
1939        }
1940      }
1941      // Fall through into normal handling
1942    case UCAL_EXTENDED_YEAR:
1943    case UCAL_MONTH:
1944      {
1945        UBool oldLenient = isLenient();
1946        setLenient(TRUE);
1947        set(field, get(field, status) + amount);
1948        pinField(UCAL_DAY_OF_MONTH, status);
1949        if(oldLenient==FALSE) {
1950          complete(status); /* force recalculate */
1951          setLenient(oldLenient);
1952        }
1953      }
1954      return;
1955
1956    case UCAL_WEEK_OF_YEAR:
1957    case UCAL_WEEK_OF_MONTH:
1958    case UCAL_DAY_OF_WEEK_IN_MONTH:
1959        delta *= kOneWeek;
1960        break;
1961
1962    case UCAL_AM_PM:
1963        delta *= 12 * kOneHour;
1964        break;
1965
1966    case UCAL_DAY_OF_MONTH:
1967    case UCAL_DAY_OF_YEAR:
1968    case UCAL_DAY_OF_WEEK:
1969    case UCAL_DOW_LOCAL:
1970    case UCAL_JULIAN_DAY:
1971        delta *= kOneDay;
1972        break;
1973
1974    case UCAL_HOUR_OF_DAY:
1975    case UCAL_HOUR:
1976        delta *= kOneHour;
1977        keepHourInvariant = FALSE;
1978        break;
1979
1980    case UCAL_MINUTE:
1981        delta *= kOneMinute;
1982        keepHourInvariant = FALSE;
1983        break;
1984
1985    case UCAL_SECOND:
1986        delta *= kOneSecond;
1987        keepHourInvariant = FALSE;
1988        break;
1989
1990    case UCAL_MILLISECOND:
1991    case UCAL_MILLISECONDS_IN_DAY:
1992        keepHourInvariant = FALSE;
1993        break;
1994
1995    default:
1996#if defined (U_DEBUG_CAL)
1997        fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s not addable",
1998            __FILE__, __LINE__, fldName(field));
1999#endif
2000        status = U_ILLEGAL_ARGUMENT_ERROR;
2001        return;
2002        //  throw new IllegalArgumentException("Calendar.add(" + fieldName(field) +
2003        //                                     ") not supported");
2004    }
2005
2006    // In order to keep the hour invariant (for fields where this is
2007    // appropriate), check the combined DST & ZONE offset before and
2008    // after the add() operation. If it changes, then adjust the millis
2009    // to compensate.
2010    int32_t prevOffset = 0;
2011    int32_t hour = 0;
2012    if (keepHourInvariant) {
2013        prevOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status);
2014        hour = internalGet(UCAL_HOUR_OF_DAY);
2015    }
2016
2017    setTimeInMillis(getTimeInMillis(status) + delta, status);
2018
2019    if (keepHourInvariant) {
2020        int32_t newOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status);
2021        if (newOffset != prevOffset) {
2022            // We have done an hour-invariant adjustment but the
2023            // combined offset has changed. We adjust millis to keep
2024            // the hour constant. In cases such as midnight after
2025            // a DST change which occurs at midnight, there is the
2026            // danger of adjusting into a different day. To avoid
2027            // this we make the adjustment only if it actually
2028            // maintains the hour.
2029
2030            // When the difference of the previous UTC offset and
2031            // the new UTC offset exceeds 1 full day, we do not want
2032            // to roll over/back the date. For now, this only happens
2033            // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452.
2034            int32_t adjAmount = prevOffset - newOffset;
2035            adjAmount = adjAmount >= 0 ? adjAmount % (int32_t)kOneDay : -(-adjAmount % (int32_t)kOneDay);
2036            if (adjAmount != 0) {
2037                double t = internalGetTime();
2038                setTimeInMillis(t + adjAmount, status);
2039                if (get(UCAL_HOUR_OF_DAY, status) != hour) {
2040                    setTimeInMillis(t, status);
2041                }
2042            }
2043        }
2044    }
2045}
2046
2047// -------------------------------------
2048int32_t Calendar::fieldDifference(UDate when, EDateFields field, UErrorCode& status) {
2049    return fieldDifference(when, (UCalendarDateFields) field, status);
2050}
2051
2052int32_t Calendar::fieldDifference(UDate targetMs, UCalendarDateFields field, UErrorCode& ec) {
2053    if (U_FAILURE(ec)) return 0;
2054    int32_t min = 0;
2055    double startMs = getTimeInMillis(ec);
2056    // Always add from the start millis.  This accomodates
2057    // operations like adding years from February 29, 2000 up to
2058    // February 29, 2004.  If 1, 1, 1, 1 is added to the year
2059    // field, the DOM gets pinned to 28 and stays there, giving an
2060    // incorrect DOM difference of 1.  We have to add 1, reset, 2,
2061    // reset, 3, reset, 4.
2062    if (startMs < targetMs) {
2063        int32_t max = 1;
2064        // Find a value that is too large
2065        while (U_SUCCESS(ec)) {
2066            setTimeInMillis(startMs, ec);
2067            add(field, max, ec);
2068            double ms = getTimeInMillis(ec);
2069            if (ms == targetMs) {
2070                return max;
2071            } else if (ms > targetMs) {
2072                break;
2073            } else if (max < INT32_MAX) {
2074                min = max;
2075                max <<= 1;
2076                if (max < 0) {
2077                    max = INT32_MAX;
2078                }
2079            } else {
2080                // Field difference too large to fit into int32_t
2081#if defined (U_DEBUG_CAL)
2082                fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
2083                    __FILE__, __LINE__, fldName(field));
2084#endif
2085                ec = U_ILLEGAL_ARGUMENT_ERROR;
2086            }
2087        }
2088        // Do a binary search
2089        while ((max - min) > 1 && U_SUCCESS(ec)) {
2090            int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX
2091            setTimeInMillis(startMs, ec);
2092            add(field, t, ec);
2093            double ms = getTimeInMillis(ec);
2094            if (ms == targetMs) {
2095                return t;
2096            } else if (ms > targetMs) {
2097                max = t;
2098            } else {
2099                min = t;
2100            }
2101        }
2102    } else if (startMs > targetMs) {
2103        int32_t max = -1;
2104        // Find a value that is too small
2105        while (U_SUCCESS(ec)) {
2106            setTimeInMillis(startMs, ec);
2107            add(field, max, ec);
2108            double ms = getTimeInMillis(ec);
2109            if (ms == targetMs) {
2110                return max;
2111            } else if (ms < targetMs) {
2112                break;
2113            } else {
2114                min = max;
2115                max <<= 1;
2116                if (max == 0) {
2117                    // Field difference too large to fit into int32_t
2118#if defined (U_DEBUG_CAL)
2119                    fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
2120                        __FILE__, __LINE__, fldName(field));
2121#endif
2122                    ec = U_ILLEGAL_ARGUMENT_ERROR;
2123                }
2124            }
2125        }
2126        // Do a binary search
2127        while ((min - max) > 1 && U_SUCCESS(ec)) {
2128            int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX
2129            setTimeInMillis(startMs, ec);
2130            add(field, t, ec);
2131            double ms = getTimeInMillis(ec);
2132            if (ms == targetMs) {
2133                return t;
2134            } else if (ms < targetMs) {
2135                max = t;
2136            } else {
2137                min = t;
2138            }
2139        }
2140    }
2141    // Set calendar to end point
2142    setTimeInMillis(startMs, ec);
2143    add(field, min, ec);
2144
2145    /* Test for buffer overflows */
2146    if(U_FAILURE(ec)) {
2147        return 0;
2148    }
2149    return min;
2150}
2151
2152// -------------------------------------
2153
2154void
2155Calendar::adoptTimeZone(TimeZone* zone)
2156{
2157    // Do nothing if passed-in zone is NULL
2158    if (zone == NULL) return;
2159
2160    // fZone should always be non-null
2161    if (fZone != NULL) delete fZone;
2162    fZone = zone;
2163
2164    // if the zone changes, we need to recompute the time fields
2165    fAreFieldsSet = FALSE;
2166}
2167
2168// -------------------------------------
2169void
2170Calendar::setTimeZone(const TimeZone& zone)
2171{
2172    adoptTimeZone(zone.clone());
2173}
2174
2175// -------------------------------------
2176
2177const TimeZone&
2178Calendar::getTimeZone() const
2179{
2180    return *fZone;
2181}
2182
2183// -------------------------------------
2184
2185TimeZone*
2186Calendar::orphanTimeZone()
2187{
2188    TimeZone *z = fZone;
2189    // we let go of the time zone; the new time zone is the system default time zone
2190    fZone = TimeZone::createDefault();
2191    return z;
2192}
2193
2194// -------------------------------------
2195
2196void
2197Calendar::setLenient(UBool lenient)
2198{
2199    fLenient = lenient;
2200}
2201
2202// -------------------------------------
2203
2204UBool
2205Calendar::isLenient() const
2206{
2207    return fLenient;
2208}
2209
2210// -------------------------------------
2211
2212void
2213Calendar::setRepeatedWallTimeOption(UCalendarWallTimeOption option)
2214{
2215    if (option == UCAL_WALLTIME_LAST || option == UCAL_WALLTIME_FIRST) {
2216        fRepeatedWallTime = option;
2217    }
2218}
2219
2220// -------------------------------------
2221
2222UCalendarWallTimeOption
2223Calendar::getRepeatedWallTimeOption(void) const
2224{
2225    return fRepeatedWallTime;
2226}
2227
2228// -------------------------------------
2229
2230void
2231Calendar::setSkippedWallTimeOption(UCalendarWallTimeOption option)
2232{
2233    fSkippedWallTime = option;
2234}
2235
2236// -------------------------------------
2237
2238UCalendarWallTimeOption
2239Calendar::getSkippedWallTimeOption(void) const
2240{
2241    return fSkippedWallTime;
2242}
2243
2244// -------------------------------------
2245
2246void
2247Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value)
2248{
2249    if (fFirstDayOfWeek != value &&
2250        value >= UCAL_SUNDAY && value <= UCAL_SATURDAY) {
2251            fFirstDayOfWeek = value;
2252            fAreFieldsSet = FALSE;
2253        }
2254}
2255
2256// -------------------------------------
2257
2258Calendar::EDaysOfWeek
2259Calendar::getFirstDayOfWeek() const
2260{
2261    return (Calendar::EDaysOfWeek)fFirstDayOfWeek;
2262}
2263
2264UCalendarDaysOfWeek
2265Calendar::getFirstDayOfWeek(UErrorCode & /*status*/) const
2266{
2267    return fFirstDayOfWeek;
2268}
2269// -------------------------------------
2270
2271void
2272Calendar::setMinimalDaysInFirstWeek(uint8_t value)
2273{
2274    // Values less than 1 have the same effect as 1; values greater
2275    // than 7 have the same effect as 7. However, we normalize values
2276    // so operator== and so forth work.
2277    if (value < 1) {
2278        value = 1;
2279    } else if (value > 7) {
2280        value = 7;
2281    }
2282    if (fMinimalDaysInFirstWeek != value) {
2283        fMinimalDaysInFirstWeek = value;
2284        fAreFieldsSet = FALSE;
2285    }
2286}
2287
2288// -------------------------------------
2289
2290uint8_t
2291Calendar::getMinimalDaysInFirstWeek() const
2292{
2293    return fMinimalDaysInFirstWeek;
2294}
2295
2296// -------------------------------------
2297// weekend functions, just dummy implementations for now (for API freeze)
2298
2299UCalendarWeekdayType
2300Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const
2301{
2302    if (U_FAILURE(status)) {
2303        return UCAL_WEEKDAY;
2304    }
2305    if (dayOfWeek < UCAL_SUNDAY || dayOfWeek > UCAL_SATURDAY) {
2306        status = U_ILLEGAL_ARGUMENT_ERROR;
2307        return UCAL_WEEKDAY;
2308    }
2309	if (fWeekendOnset == fWeekendCease) {
2310		if (dayOfWeek != fWeekendOnset)
2311			return UCAL_WEEKDAY;
2312		return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET;
2313	}
2314    if (fWeekendOnset < fWeekendCease) {
2315        if (dayOfWeek < fWeekendOnset || dayOfWeek > fWeekendCease) {
2316            return UCAL_WEEKDAY;
2317        }
2318    } else {
2319        if (dayOfWeek > fWeekendCease && dayOfWeek < fWeekendOnset) {
2320            return UCAL_WEEKDAY;
2321        }
2322    }
2323    if (dayOfWeek == fWeekendOnset) {
2324        return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET;
2325    }
2326    if (dayOfWeek == fWeekendCease) {
2327        return (fWeekendCeaseMillis >= 86400000) ? UCAL_WEEKEND : UCAL_WEEKEND_CEASE;
2328    }
2329    return UCAL_WEEKEND;
2330}
2331
2332int32_t
2333Calendar::getWeekendTransition(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const
2334{
2335    if (U_FAILURE(status)) {
2336        return 0;
2337    }
2338    if (dayOfWeek == fWeekendOnset) {
2339        return fWeekendOnsetMillis;
2340    } else if (dayOfWeek == fWeekendCease) {
2341        return fWeekendCeaseMillis;
2342    }
2343    status = U_ILLEGAL_ARGUMENT_ERROR;
2344    return 0;
2345}
2346
2347UBool
2348Calendar::isWeekend(UDate date, UErrorCode &status) const
2349{
2350    if (U_FAILURE(status)) {
2351        return FALSE;
2352    }
2353    // clone the calendar so we don't mess with the real one.
2354    Calendar *work = (Calendar*)this->clone();
2355    if (work == NULL) {
2356        status = U_MEMORY_ALLOCATION_ERROR;
2357        return FALSE;
2358    }
2359    UBool result = FALSE;
2360    work->setTime(date, status);
2361    if (U_SUCCESS(status)) {
2362        result = work->isWeekend();
2363    }
2364    delete work;
2365    return result;
2366}
2367
2368UBool
2369Calendar::isWeekend(void) const
2370{
2371    UErrorCode status = U_ZERO_ERROR;
2372    UCalendarDaysOfWeek dayOfWeek = (UCalendarDaysOfWeek)get(UCAL_DAY_OF_WEEK, status);
2373    UCalendarWeekdayType dayType = getDayOfWeekType(dayOfWeek, status);
2374    if (U_SUCCESS(status)) {
2375        switch (dayType) {
2376            case UCAL_WEEKDAY:
2377                return FALSE;
2378            case UCAL_WEEKEND:
2379                return TRUE;
2380            case UCAL_WEEKEND_ONSET:
2381            case UCAL_WEEKEND_CEASE:
2382                // Use internalGet() because the above call to get() populated all fields.
2383                {
2384                    int32_t millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY);
2385                    int32_t transitionMillis = getWeekendTransition(dayOfWeek, status);
2386                    if (U_SUCCESS(status)) {
2387                        return (dayType == UCAL_WEEKEND_ONSET)?
2388                            (millisInDay >= transitionMillis):
2389                            (millisInDay <  transitionMillis);
2390                    }
2391                    // else fall through, return FALSE
2392                }
2393            default:
2394                break;
2395        }
2396    }
2397    return FALSE;
2398}
2399
2400// ------------------------------------- limits
2401
2402int32_t
2403Calendar::getMinimum(EDateFields field) const {
2404    return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MINIMUM);
2405}
2406
2407int32_t
2408Calendar::getMinimum(UCalendarDateFields field) const
2409{
2410    return getLimit(field,UCAL_LIMIT_MINIMUM);
2411}
2412
2413// -------------------------------------
2414int32_t
2415Calendar::getMaximum(EDateFields field) const
2416{
2417    return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MAXIMUM);
2418}
2419
2420int32_t
2421Calendar::getMaximum(UCalendarDateFields field) const
2422{
2423    return getLimit(field,UCAL_LIMIT_MAXIMUM);
2424}
2425
2426// -------------------------------------
2427int32_t
2428Calendar::getGreatestMinimum(EDateFields field) const
2429{
2430    return getLimit((UCalendarDateFields)field,UCAL_LIMIT_GREATEST_MINIMUM);
2431}
2432
2433int32_t
2434Calendar::getGreatestMinimum(UCalendarDateFields field) const
2435{
2436    return getLimit(field,UCAL_LIMIT_GREATEST_MINIMUM);
2437}
2438
2439// -------------------------------------
2440int32_t
2441Calendar::getLeastMaximum(EDateFields field) const
2442{
2443    return getLimit((UCalendarDateFields) field,UCAL_LIMIT_LEAST_MAXIMUM);
2444}
2445
2446int32_t
2447Calendar::getLeastMaximum(UCalendarDateFields field) const
2448{
2449    return getLimit( field,UCAL_LIMIT_LEAST_MAXIMUM);
2450}
2451
2452// -------------------------------------
2453int32_t
2454Calendar::getActualMinimum(EDateFields field, UErrorCode& status) const
2455{
2456    return getActualMinimum((UCalendarDateFields) field, status);
2457}
2458
2459int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) const {
2460    switch (field) {
2461    case UCAL_DAY_OF_WEEK:
2462    case UCAL_AM_PM:
2463    case UCAL_HOUR:
2464    case UCAL_HOUR_OF_DAY:
2465    case UCAL_MINUTE:
2466    case UCAL_SECOND:
2467    case UCAL_MILLISECOND:
2468    case UCAL_ZONE_OFFSET:
2469    case UCAL_DST_OFFSET:
2470    case UCAL_DOW_LOCAL:
2471    case UCAL_JULIAN_DAY:
2472    case UCAL_MILLISECONDS_IN_DAY:
2473    case UCAL_IS_LEAP_MONTH:
2474        return kCalendarLimits[field][limitType];
2475
2476    case UCAL_WEEK_OF_MONTH:
2477        {
2478            int32_t limit;
2479            if (limitType == UCAL_LIMIT_MINIMUM) {
2480                limit = getMinimalDaysInFirstWeek() == 1 ? 1 : 0;
2481            } else if (limitType == UCAL_LIMIT_GREATEST_MINIMUM) {
2482                limit = 1;
2483            } else {
2484                int32_t minDaysInFirst = getMinimalDaysInFirstWeek();
2485                int32_t daysInMonth = handleGetLimit(UCAL_DAY_OF_MONTH, limitType);
2486                if (limitType == UCAL_LIMIT_LEAST_MAXIMUM) {
2487                    limit = (daysInMonth + (7 - minDaysInFirst)) / 7;
2488                } else { // limitType == UCAL_LIMIT_MAXIMUM
2489                    limit = (daysInMonth + 6 + (7 - minDaysInFirst)) / 7;
2490                }
2491            }
2492            return limit;
2493        }
2494    default:
2495        return handleGetLimit(field, limitType);
2496    }
2497}
2498
2499
2500int32_t
2501Calendar::getActualMinimum(UCalendarDateFields field, UErrorCode& status) const
2502{
2503    int32_t fieldValue = getGreatestMinimum(field);
2504    int32_t endValue = getMinimum(field);
2505
2506    // if we know that the minimum value is always the same, just return it
2507    if (fieldValue == endValue) {
2508        return fieldValue;
2509    }
2510
2511    // clone the calendar so we don't mess with the real one, and set it to
2512    // accept anything for the field values
2513    Calendar *work = (Calendar*)this->clone();
2514    if (work == NULL) {
2515        status = U_MEMORY_ALLOCATION_ERROR;
2516        return 0;
2517    }
2518    work->setLenient(TRUE);
2519
2520    // now try each value from getLeastMaximum() to getMaximum() one by one until
2521    // we get a value that normalizes to another value.  The last value that
2522    // normalizes to itself is the actual minimum for the current date
2523    int32_t result = fieldValue;
2524
2525    do {
2526        work->set(field, fieldValue);
2527        if (work->get(field, status) != fieldValue) {
2528            break;
2529        }
2530        else {
2531            result = fieldValue;
2532            fieldValue--;
2533        }
2534    } while (fieldValue >= endValue);
2535
2536    delete work;
2537
2538    /* Test for buffer overflows */
2539    if(U_FAILURE(status)) {
2540        return 0;
2541    }
2542    return result;
2543}
2544
2545// -------------------------------------
2546
2547
2548
2549/**
2550* Ensure that each field is within its valid range by calling {@link
2551* #validateField(int)} on each field that has been set.  This method
2552* should only be called if this calendar is not lenient.
2553* @see #isLenient
2554* @see #validateField(int)
2555*/
2556void Calendar::validateFields(UErrorCode &status) {
2557    for (int32_t field = 0; U_SUCCESS(status) && (field < UCAL_FIELD_COUNT); field++) {
2558        if (fStamp[field] >= kMinimumUserStamp) {
2559            validateField((UCalendarDateFields)field, status);
2560        }
2561    }
2562}
2563
2564/**
2565* Validate a single field of this calendar.  Subclasses should
2566* override this method to validate any calendar-specific fields.
2567* Generic fields can be handled by
2568* <code>Calendar.validateField()</code>.
2569* @see #validateField(int, int, int)
2570*/
2571void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) {
2572    int32_t y;
2573    switch (field) {
2574    case UCAL_DAY_OF_MONTH:
2575        y = handleGetExtendedYear();
2576        validateField(field, 1, handleGetMonthLength(y, internalGet(UCAL_MONTH)), status);
2577        break;
2578    case UCAL_DAY_OF_YEAR:
2579        y = handleGetExtendedYear();
2580        validateField(field, 1, handleGetYearLength(y), status);
2581        break;
2582    case UCAL_DAY_OF_WEEK_IN_MONTH:
2583        if (internalGet(field) == 0) {
2584#if defined (U_DEBUG_CAL)
2585            fprintf(stderr, "%s:%d: ILLEGAL ARG because DOW in month cannot be 0\n",
2586                __FILE__, __LINE__);
2587#endif
2588            status = U_ILLEGAL_ARGUMENT_ERROR; // "DAY_OF_WEEK_IN_MONTH cannot be zero"
2589            return;
2590        }
2591        validateField(field, getMinimum(field), getMaximum(field), status);
2592        break;
2593    default:
2594        validateField(field, getMinimum(field), getMaximum(field), status);
2595        break;
2596    }
2597}
2598
2599/**
2600* Validate a single field of this calendar given its minimum and
2601* maximum allowed value.  If the field is out of range, throw a
2602* descriptive <code>IllegalArgumentException</code>.  Subclasses may
2603* use this method in their implementation of {@link
2604* #validateField(int)}.
2605*/
2606void Calendar::validateField(UCalendarDateFields field, int32_t min, int32_t max, UErrorCode& status)
2607{
2608    int32_t value = fFields[field];
2609    if (value < min || value > max) {
2610#if defined (U_DEBUG_CAL)
2611        fprintf(stderr, "%s:%d: ILLEGAL ARG because of field %s out of range %d..%d  at %d\n",
2612            __FILE__, __LINE__,fldName(field),min,max,value);
2613#endif
2614        status = U_ILLEGAL_ARGUMENT_ERROR;
2615        return;
2616    }
2617}
2618
2619// -------------------------
2620
2621const UFieldResolutionTable* Calendar::getFieldResolutionTable() const {
2622    return kDatePrecedence;
2623}
2624
2625
2626UCalendarDateFields Calendar::newerField(UCalendarDateFields defaultField, UCalendarDateFields alternateField) const
2627{
2628    if (fStamp[alternateField] > fStamp[defaultField]) {
2629        return alternateField;
2630    }
2631    return defaultField;
2632}
2633
2634UCalendarDateFields Calendar::resolveFields(const UFieldResolutionTable* precedenceTable) {
2635    int32_t bestField = UCAL_FIELD_COUNT;
2636    int32_t tempBestField;
2637    for (int32_t g=0; precedenceTable[g][0][0] != -1 && (bestField == UCAL_FIELD_COUNT); ++g) {
2638        int32_t bestStamp = kUnset;
2639        for (int32_t l=0; precedenceTable[g][l][0] != -1; ++l) {
2640            int32_t lineStamp = kUnset;
2641            // Skip over first entry if it is negative
2642            for (int32_t i=((precedenceTable[g][l][0]>=kResolveRemap)?1:0); precedenceTable[g][l][i]!=-1; ++i) {
2643                U_ASSERT(precedenceTable[g][l][i] < UCAL_FIELD_COUNT);
2644                int32_t s = fStamp[precedenceTable[g][l][i]];
2645                // If any field is unset then don't use this line
2646                if (s == kUnset) {
2647                    goto linesInGroup;
2648                } else if(s > lineStamp) {
2649                    lineStamp = s;
2650                }
2651            }
2652            // Record new maximum stamp & field no.
2653            if (lineStamp > bestStamp) {
2654                tempBestField = precedenceTable[g][l][0]; // First field refers to entire line
2655                if (tempBestField >= kResolveRemap) {
2656                    tempBestField &= (kResolveRemap-1);
2657                    // This check is needed to resolve some issues with UCAL_YEAR precedence mapping
2658                    if (tempBestField != UCAL_DATE || (fStamp[UCAL_WEEK_OF_MONTH] < fStamp[tempBestField])) {
2659                        bestField = tempBestField;
2660                    }
2661                } else {
2662                    bestField = tempBestField;
2663                }
2664
2665                if (bestField == tempBestField) {
2666                    bestStamp = lineStamp;
2667                }
2668            }
2669linesInGroup:
2670            ;
2671        }
2672    }
2673    return (UCalendarDateFields)bestField;
2674}
2675
2676const UFieldResolutionTable Calendar::kDatePrecedence[] =
2677{
2678    {
2679        { UCAL_DAY_OF_MONTH, kResolveSTOP },
2680        { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP },
2681        { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
2682        { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
2683        { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP },
2684        { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
2685        { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
2686        { UCAL_DAY_OF_YEAR, kResolveSTOP },
2687        { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_YEAR, kResolveSTOP },  // if YEAR is set over YEAR_WOY use DAY_OF_MONTH
2688        { kResolveRemap | UCAL_WEEK_OF_YEAR, UCAL_YEAR_WOY, kResolveSTOP },  // if YEAR_WOY is set,  calc based on WEEK_OF_YEAR
2689        { kResolveSTOP }
2690    },
2691    {
2692        { UCAL_WEEK_OF_YEAR, kResolveSTOP },
2693        { UCAL_WEEK_OF_MONTH, kResolveSTOP },
2694        { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP },
2695        { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
2696        { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
2697        { kResolveSTOP }
2698    },
2699    {{kResolveSTOP}}
2700};
2701
2702
2703const UFieldResolutionTable Calendar::kDOWPrecedence[] =
2704{
2705    {
2706        { UCAL_DAY_OF_WEEK,kResolveSTOP, kResolveSTOP },
2707        { UCAL_DOW_LOCAL,kResolveSTOP, kResolveSTOP },
2708        {kResolveSTOP}
2709    },
2710    {{kResolveSTOP}}
2711};
2712
2713// precedence for calculating a year
2714const UFieldResolutionTable Calendar::kYearPrecedence[] =
2715{
2716    {
2717        { UCAL_YEAR, kResolveSTOP },
2718        { UCAL_EXTENDED_YEAR, kResolveSTOP },
2719        { UCAL_YEAR_WOY, UCAL_WEEK_OF_YEAR, kResolveSTOP },  // YEAR_WOY is useless without WEEK_OF_YEAR
2720        { kResolveSTOP }
2721    },
2722    {{kResolveSTOP}}
2723};
2724
2725
2726// -------------------------
2727
2728
2729void Calendar::computeTime(UErrorCode& status) {
2730    if (!isLenient()) {
2731        validateFields(status);
2732        if (U_FAILURE(status)) {
2733            return;
2734        }
2735    }
2736
2737    // Compute the Julian day
2738    int32_t julianDay = computeJulianDay();
2739
2740    double millis = Grego::julianDayToMillis(julianDay);
2741
2742#if defined (U_DEBUG_CAL)
2743    //  int32_t julianInsanityCheck =  (int32_t)ClockMath::floorDivide(millis, kOneDay);
2744    //  julianInsanityCheck += kEpochStartAsJulianDay;
2745    //  if(1 || julianInsanityCheck != julianDay) {
2746    //    fprintf(stderr, "%s:%d- D'oh- computed jules %d, to mills (%s)%.lf, recomputed %d\n",
2747    //            __FILE__, __LINE__, julianDay, millis<0.0?"NEG":"", millis, julianInsanityCheck);
2748    //  }
2749#endif
2750
2751    int32_t millisInDay;
2752
2753    // We only use MILLISECONDS_IN_DAY if it has been set by the user.
2754    // This makes it possible for the caller to set the calendar to a
2755    // time and call clear(MONTH) to reset the MONTH to January.  This
2756    // is legacy behavior.  Without this, clear(MONTH) has no effect,
2757    // since the internally set JULIAN_DAY is used.
2758    if (fStamp[UCAL_MILLISECONDS_IN_DAY] >= ((int32_t)kMinimumUserStamp) &&
2759            newestStamp(UCAL_AM_PM, UCAL_MILLISECOND, kUnset) <= fStamp[UCAL_MILLISECONDS_IN_DAY]) {
2760        millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY);
2761    } else {
2762        millisInDay = computeMillisInDay();
2763    }
2764
2765    UDate t = 0;
2766    if (fStamp[UCAL_ZONE_OFFSET] >= ((int32_t)kMinimumUserStamp) || fStamp[UCAL_DST_OFFSET] >= ((int32_t)kMinimumUserStamp)) {
2767        t = millis + millisInDay - (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET));
2768    } else {
2769        // Compute the time zone offset and DST offset.  There are two potential
2770        // ambiguities here.  We'll assume a 2:00 am (wall time) switchover time
2771        // for discussion purposes here.
2772        //
2773        // 1. The positive offset change such as transition into DST.
2774        //    Here, a designated time of 2:00 am - 2:59 am does not actually exist.
2775        //    For this case, skippedWallTime option specifies the behavior.
2776        //    For example, 2:30 am is interpreted as;
2777        //      - WALLTIME_LAST(default): 3:30 am (DST) (interpreting 2:30 am as 31 minutes after 1:59 am (STD))
2778        //      - WALLTIME_FIRST: 1:30 am (STD) (interpreting 2:30 am as 30 minutes before 3:00 am (DST))
2779        //      - WALLTIME_NEXT_VALID: 3:00 am (DST) (next valid time after 2:30 am on a wall clock)
2780        // 2. The negative offset change such as transition out of DST.
2781        //    Here, a designated time of 1:00 am - 1:59 am can be in standard or DST.  Both are valid
2782        //    representations (the rep jumps from 1:59:59 DST to 1:00:00 Std).
2783        //    For this case, repeatedWallTime option specifies the behavior.
2784        //    For example, 1:30 am is interpreted as;
2785        //      - WALLTIME_LAST(default): 1:30 am (STD) - latter occurrence
2786        //      - WALLTIME_FIRST: 1:30 am (DST) - former occurrence
2787        //
2788        // In addition to above, when calendar is strict (not default), wall time falls into
2789        // the skipped time range will be processed as an error case.
2790        //
2791        // These special cases are mostly handled in #computeZoneOffset(long), except WALLTIME_NEXT_VALID
2792        // at positive offset change. The protected method computeZoneOffset(long) is exposed to Calendar
2793        // subclass implementations and marked as @stable. Strictly speaking, WALLTIME_NEXT_VALID
2794        // should be also handled in the same place, but we cannot change the code flow without deprecating
2795        // the protected method.
2796        //
2797        // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
2798        // or DST_OFFSET fields; then we use those fields.
2799
2800        if (!isLenient() || fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID) {
2801            // When strict, invalidate a wall time falls into a skipped wall time range.
2802            // When lenient and skipped wall time option is WALLTIME_NEXT_VALID,
2803            // the result time will be adjusted to the next valid time (on wall clock).
2804            int32_t zoneOffset = computeZoneOffset(millis, millisInDay, status);
2805            UDate tmpTime = millis + millisInDay - zoneOffset;
2806
2807            int32_t raw, dst;
2808            fZone->getOffset(tmpTime, FALSE, raw, dst, status);
2809
2810            if (U_SUCCESS(status)) {
2811                // zoneOffset != (raw + dst) only when the given wall time fall into
2812                // a skipped wall time range caused by positive zone offset transition.
2813                if (zoneOffset != (raw + dst)) {
2814                    if (!isLenient()) {
2815                        status = U_ILLEGAL_ARGUMENT_ERROR;
2816                    } else {
2817                        U_ASSERT(fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID);
2818                        // Adjust time to the next valid wall clock time.
2819                        // At this point, tmpTime is on or after the zone offset transition causing
2820                        // the skipped time range.
2821
2822                        BasicTimeZone *btz = getBasicTimeZone();
2823                        if (btz) {
2824                            TimeZoneTransition transition;
2825                            UBool hasTransition = btz->getPreviousTransition(tmpTime, TRUE, transition);
2826                            if (hasTransition) {
2827                                t = transition.getTime();
2828                            } else {
2829                                // Could not find any transitions.
2830                                // Note: This should never happen.
2831                                status = U_INTERNAL_PROGRAM_ERROR;
2832                            }
2833                        } else {
2834                            // If not BasicTimeZone, return unsupported error for now.
2835                            // TODO: We may support non-BasicTimeZone in future.
2836                            status = U_UNSUPPORTED_ERROR;
2837                        }
2838                    }
2839                } else {
2840                    t = tmpTime;
2841                }
2842            }
2843        } else {
2844            t = millis + millisInDay - computeZoneOffset(millis, millisInDay, status);
2845        }
2846    }
2847    if (U_SUCCESS(status)) {
2848        internalSetTime(t);
2849    }
2850}
2851
2852/**
2853* Compute the milliseconds in the day from the fields.  This is a
2854* value from 0 to 23:59:59.999 inclusive, unless fields are out of
2855* range, in which case it can be an arbitrary value.  This value
2856* reflects local zone wall time.
2857* @stable ICU 2.0
2858*/
2859int32_t Calendar::computeMillisInDay() {
2860  // Do the time portion of the conversion.
2861
2862    int32_t millisInDay = 0;
2863
2864    // Find the best set of fields specifying the time of day.  There
2865    // are only two possibilities here; the HOUR_OF_DAY or the
2866    // AM_PM and the HOUR.
2867    int32_t hourOfDayStamp = fStamp[UCAL_HOUR_OF_DAY];
2868    int32_t hourStamp = (fStamp[UCAL_HOUR] > fStamp[UCAL_AM_PM])?fStamp[UCAL_HOUR]:fStamp[UCAL_AM_PM];
2869    int32_t bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp;
2870
2871    // Hours
2872    if (bestStamp != kUnset) {
2873        if (bestStamp == hourOfDayStamp) {
2874            // Don't normalize here; let overflow bump into the next period.
2875            // This is consistent with how we handle other fields.
2876            millisInDay += internalGet(UCAL_HOUR_OF_DAY);
2877        } else {
2878            // Don't normalize here; let overflow bump into the next period.
2879            // This is consistent with how we handle other fields.
2880            millisInDay += internalGet(UCAL_HOUR);
2881            millisInDay += 12 * internalGet(UCAL_AM_PM); // Default works for unset AM_PM
2882        }
2883    }
2884
2885    // We use the fact that unset == 0; we start with millisInDay
2886    // == HOUR_OF_DAY.
2887    millisInDay *= 60;
2888    millisInDay += internalGet(UCAL_MINUTE); // now have minutes
2889    millisInDay *= 60;
2890    millisInDay += internalGet(UCAL_SECOND); // now have seconds
2891    millisInDay *= 1000;
2892    millisInDay += internalGet(UCAL_MILLISECOND); // now have millis
2893
2894    return millisInDay;
2895}
2896
2897/**
2898* This method can assume EXTENDED_YEAR has been set.
2899* @param millis milliseconds of the date fields
2900* @param millisInDay milliseconds of the time fields; may be out
2901* or range.
2902* @stable ICU 2.0
2903*/
2904int32_t Calendar::computeZoneOffset(double millis, int32_t millisInDay, UErrorCode &ec) {
2905    int32_t rawOffset, dstOffset;
2906    UDate wall = millis + millisInDay;
2907    BasicTimeZone* btz = getBasicTimeZone();
2908    if (btz) {
2909        int duplicatedTimeOpt = (fRepeatedWallTime == UCAL_WALLTIME_FIRST) ? BasicTimeZone::kFormer : BasicTimeZone::kLatter;
2910        int nonExistingTimeOpt = (fSkippedWallTime == UCAL_WALLTIME_FIRST) ? BasicTimeZone::kLatter : BasicTimeZone::kFormer;
2911        btz->getOffsetFromLocal(wall, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, ec);
2912    } else {
2913        const TimeZone& tz = getTimeZone();
2914        // By default, TimeZone::getOffset behaves UCAL_WALLTIME_LAST for both.
2915        tz.getOffset(wall, TRUE, rawOffset, dstOffset, ec);
2916
2917        UBool sawRecentNegativeShift = FALSE;
2918        if (fRepeatedWallTime == UCAL_WALLTIME_FIRST) {
2919            // Check if the given wall time falls into repeated time range
2920            UDate tgmt = wall - (rawOffset + dstOffset);
2921
2922            // Any negative zone transition within last 6 hours?
2923            // Note: The maximum historic negative zone transition is -3 hours in the tz database.
2924            // 6 hour window would be sufficient for this purpose.
2925            int32_t tmpRaw, tmpDst;
2926            tz.getOffset(tgmt - 6*60*60*1000, FALSE, tmpRaw, tmpDst, ec);
2927            int32_t offsetDelta = (rawOffset + dstOffset) - (tmpRaw + tmpDst);
2928
2929            U_ASSERT(offsetDelta < -6*60*60*1000);
2930            if (offsetDelta < 0) {
2931                sawRecentNegativeShift = TRUE;
2932                // Negative shift within last 6 hours. When UCAL_WALLTIME_FIRST is used and the given wall time falls
2933                // into the repeated time range, use offsets before the transition.
2934                // Note: If it does not fall into the repeated time range, offsets remain unchanged below.
2935                tz.getOffset(wall + offsetDelta, TRUE, rawOffset, dstOffset, ec);
2936            }
2937        }
2938        if (!sawRecentNegativeShift && fSkippedWallTime == UCAL_WALLTIME_FIRST) {
2939            // When skipped wall time option is WALLTIME_FIRST,
2940            // recalculate offsets from the resolved time (non-wall).
2941            // When the given wall time falls into skipped wall time,
2942            // the offsets will be based on the zone offsets AFTER
2943            // the transition (which means, earliest possibe interpretation).
2944            UDate tgmt = wall - (rawOffset + dstOffset);
2945            tz.getOffset(tgmt, FALSE, rawOffset, dstOffset, ec);
2946        }
2947    }
2948    return rawOffset + dstOffset;
2949}
2950
2951int32_t Calendar::computeJulianDay()
2952{
2953    // We want to see if any of the date fields is newer than the
2954    // JULIAN_DAY.  If not, then we use JULIAN_DAY.  If so, then we do
2955    // the normal resolution.  We only use JULIAN_DAY if it has been
2956    // set by the user.  This makes it possible for the caller to set
2957    // the calendar to a time and call clear(MONTH) to reset the MONTH
2958    // to January.  This is legacy behavior.  Without this,
2959    // clear(MONTH) has no effect, since the internally set JULIAN_DAY
2960    // is used.
2961    if (fStamp[UCAL_JULIAN_DAY] >= (int32_t)kMinimumUserStamp) {
2962        int32_t bestStamp = newestStamp(UCAL_ERA, UCAL_DAY_OF_WEEK_IN_MONTH, kUnset);
2963        bestStamp = newestStamp(UCAL_YEAR_WOY, UCAL_EXTENDED_YEAR, bestStamp);
2964        if (bestStamp <= fStamp[UCAL_JULIAN_DAY]) {
2965            return internalGet(UCAL_JULIAN_DAY);
2966        }
2967    }
2968
2969    UCalendarDateFields bestField = resolveFields(getFieldResolutionTable());
2970    if (bestField == UCAL_FIELD_COUNT) {
2971        bestField = UCAL_DAY_OF_MONTH;
2972    }
2973
2974    return handleComputeJulianDay(bestField);
2975}
2976
2977// -------------------------------------------
2978
2979int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField)  {
2980    UBool useMonth = (bestField == UCAL_DAY_OF_MONTH ||
2981        bestField == UCAL_WEEK_OF_MONTH ||
2982        bestField == UCAL_DAY_OF_WEEK_IN_MONTH);
2983    int32_t year;
2984
2985    if (bestField == UCAL_WEEK_OF_YEAR) {
2986        year = internalGet(UCAL_YEAR_WOY, handleGetExtendedYear());
2987        internalSet(UCAL_EXTENDED_YEAR, year);
2988    } else {
2989        year = handleGetExtendedYear();
2990        internalSet(UCAL_EXTENDED_YEAR, year);
2991    }
2992
2993#if defined (U_DEBUG_CAL)
2994    fprintf(stderr, "%s:%d: bestField= %s - y=%d\n", __FILE__, __LINE__, fldName(bestField), year);
2995#endif
2996
2997    // Get the Julian day of the day BEFORE the start of this year.
2998    // If useMonth is true, get the day before the start of the month.
2999
3000    // give calendar subclass a chance to have a default 'first' month
3001    int32_t month;
3002
3003    if(isSet(UCAL_MONTH)) {
3004        month = internalGet(UCAL_MONTH);
3005    } else {
3006        month = getDefaultMonthInYear(year);
3007    }
3008
3009    int32_t julianDay = handleComputeMonthStart(year, useMonth ? month : 0, useMonth);
3010
3011    if (bestField == UCAL_DAY_OF_MONTH) {
3012
3013        // give calendar subclass a chance to have a default 'first' dom
3014        int32_t dayOfMonth;
3015        if(isSet(UCAL_DAY_OF_MONTH)) {
3016            dayOfMonth = internalGet(UCAL_DAY_OF_MONTH,1);
3017        } else {
3018            dayOfMonth = getDefaultDayInMonth(year, month);
3019        }
3020        return julianDay + dayOfMonth;
3021    }
3022
3023    if (bestField == UCAL_DAY_OF_YEAR) {
3024        return julianDay + internalGet(UCAL_DAY_OF_YEAR);
3025    }
3026
3027    int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw
3028
3029    // At this point julianDay is the 0-based day BEFORE the first day of
3030    // January 1, year 1 of the given calendar.  If julianDay == 0, it
3031    // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
3032    // or Gregorian). (or it is before the month we are in, if useMonth is True)
3033
3034    // At this point we need to process the WEEK_OF_MONTH or
3035    // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
3036    // First, perform initial shared computations.  These locate the
3037    // first week of the period.
3038
3039    // Get the 0-based localized DOW of day one of the month or year.
3040    // Valid range 0..6.
3041    int32_t first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek;
3042    if (first < 0) {
3043        first += 7;
3044    }
3045
3046    int32_t dowLocal = getLocalDOW();
3047
3048    // Find the first target DOW (dowLocal) in the month or year.
3049    // Actually, it may be just before the first of the month or year.
3050    // It will be an integer from -5..7.
3051    int32_t date = 1 - first + dowLocal;
3052
3053    if (bestField == UCAL_DAY_OF_WEEK_IN_MONTH) {
3054        // Adjust the target DOW to be in the month or year.
3055        if (date < 1) {
3056            date += 7;
3057        }
3058
3059        // The only trickiness occurs if the day-of-week-in-month is
3060        // negative.
3061        int32_t dim = internalGet(UCAL_DAY_OF_WEEK_IN_MONTH, 1);
3062        if (dim >= 0) {
3063            date += 7*(dim - 1);
3064
3065        } else {
3066            // Move date to the last of this day-of-week in this month,
3067            // then back up as needed.  If dim==-1, we don't back up at
3068            // all.  If dim==-2, we back up once, etc.  Don't back up
3069            // past the first of the given day-of-week in this month.
3070            // Note that we handle -2, -3, etc. correctly, even though
3071            // values < -1 are technically disallowed.
3072            int32_t m = internalGet(UCAL_MONTH, UCAL_JANUARY);
3073            int32_t monthLength = handleGetMonthLength(year, m);
3074            date += ((monthLength - date) / 7 + dim + 1) * 7;
3075        }
3076    } else {
3077#if defined (U_DEBUG_CAL)
3078        fprintf(stderr, "%s:%d - bf= %s\n", __FILE__, __LINE__, fldName(bestField));
3079#endif
3080
3081        if(bestField == UCAL_WEEK_OF_YEAR) {  // ------------------------------------- WOY -------------
3082            if(!isSet(UCAL_YEAR_WOY) ||  // YWOY not set at all or
3083                ( (resolveFields(kYearPrecedence) != UCAL_YEAR_WOY) // YWOY doesn't have precedence
3084                && (fStamp[UCAL_YEAR_WOY]!=kInternallySet) ) ) // (excluding where all fields are internally set - then YWOY is used)
3085            {
3086                // need to be sure to stay in 'real' year.
3087                int32_t woy = internalGet(bestField);
3088
3089                int32_t nextJulianDay = handleComputeMonthStart(year+1, 0, FALSE); // jd of day before jan 1
3090                int32_t nextFirst = julianDayToDayOfWeek(nextJulianDay + 1) - firstDayOfWeek;
3091
3092                if (nextFirst < 0) { // 0..6 ldow of Jan 1
3093                    nextFirst += 7;
3094                }
3095
3096                if(woy==1) {  // FIRST WEEK ---------------------------------
3097#if defined (U_DEBUG_CAL)
3098                    fprintf(stderr, "%s:%d - woy=%d, yp=%d, nj(%d)=%d, nf=%d", __FILE__, __LINE__,
3099                        internalGet(bestField), resolveFields(kYearPrecedence), year+1,
3100                        nextJulianDay, nextFirst);
3101
3102                    fprintf(stderr, " next: %d DFW,  min=%d   \n", (7-nextFirst), getMinimalDaysInFirstWeek() );
3103#endif
3104
3105                    // nextFirst is now the localized DOW of Jan 1  of y-woy+1
3106                    if((nextFirst > 0) &&   // Jan 1 starts on FDOW
3107                        (7-nextFirst) >= getMinimalDaysInFirstWeek()) // or enough days in the week
3108                    {
3109                        // Jan 1 of (yearWoy+1) is in yearWoy+1 - recalculate JD to next year
3110#if defined (U_DEBUG_CAL)
3111                        fprintf(stderr, "%s:%d - was going to move JD from %d to %d [d%d]\n", __FILE__, __LINE__,
3112                            julianDay, nextJulianDay, (nextJulianDay-julianDay));
3113#endif
3114                        julianDay = nextJulianDay;
3115
3116                        // recalculate 'first' [0-based local dow of jan 1]
3117                        first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek;
3118                        if (first < 0) {
3119                            first += 7;
3120                        }
3121                        // recalculate date.
3122                        date = 1 - first + dowLocal;
3123                    }
3124                } else if(woy>=getLeastMaximum(bestField)) {
3125                    // could be in the last week- find out if this JD would overstep
3126                    int32_t testDate = date;
3127                    if ((7 - first) < getMinimalDaysInFirstWeek()) {
3128                        testDate += 7;
3129                    }
3130
3131                    // Now adjust for the week number.
3132                    testDate += 7 * (woy - 1);
3133
3134#if defined (U_DEBUG_CAL)
3135                    fprintf(stderr, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n",
3136                        __FILE__, __LINE__, year, year-1, testDate, julianDay+testDate, nextJulianDay);
3137#endif
3138                    if(julianDay+testDate > nextJulianDay) { // is it past Dec 31?  (nextJulianDay is day BEFORE year+1's  Jan 1)
3139                        // Fire up the calculating engines.. retry YWOY = (year-1)
3140                        julianDay = handleComputeMonthStart(year-1, 0, FALSE); // jd before Jan 1 of previous year
3141                        first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; // 0 based local dow   of first week
3142
3143                        if(first < 0) { // 0..6
3144                            first += 7;
3145                        }
3146                        date = 1 - first + dowLocal;
3147
3148#if defined (U_DEBUG_CAL)
3149                        fprintf(stderr, "%s:%d - date now %d, jd%d, ywoy%d\n",
3150                            __FILE__, __LINE__, date, julianDay, year-1);
3151#endif
3152
3153
3154                    } /* correction needed */
3155                } /* leastmaximum */
3156            } /* resolvefields(year) != year_woy */
3157        } /* bestfield != week_of_year */
3158
3159        // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR)
3160        // Adjust for minimal days in first week
3161        if ((7 - first) < getMinimalDaysInFirstWeek()) {
3162            date += 7;
3163        }
3164
3165        // Now adjust for the week number.
3166        date += 7 * (internalGet(bestField) - 1);
3167    }
3168
3169    return julianDay + date;
3170}
3171
3172int32_t
3173Calendar::getDefaultMonthInYear(int32_t /*eyear*/)
3174{
3175    return 0;
3176}
3177
3178int32_t
3179Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/)
3180{
3181    return 1;
3182}
3183
3184
3185int32_t Calendar::getLocalDOW()
3186{
3187  // Get zero-based localized DOW, valid range 0..6.  This is the DOW
3188    // we are looking for.
3189    int32_t dowLocal = 0;
3190    switch (resolveFields(kDOWPrecedence)) {
3191    case UCAL_DAY_OF_WEEK:
3192        dowLocal = internalGet(UCAL_DAY_OF_WEEK) - fFirstDayOfWeek;
3193        break;
3194    case UCAL_DOW_LOCAL:
3195        dowLocal = internalGet(UCAL_DOW_LOCAL) - 1;
3196        break;
3197    default:
3198        break;
3199    }
3200    dowLocal = dowLocal % 7;
3201    if (dowLocal < 0) {
3202        dowLocal += 7;
3203    }
3204    return dowLocal;
3205}
3206
3207int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy)
3208{
3209    // We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine
3210    // what year we fall in, so that other code can set it properly.
3211    // (code borrowed from computeWeekFields and handleComputeJulianDay)
3212    //return yearWoy;
3213
3214    // First, we need a reliable DOW.
3215    UCalendarDateFields bestField = resolveFields(kDatePrecedence); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields
3216
3217    // Now, a local DOW
3218    int32_t dowLocal = getLocalDOW(); // 0..6
3219    int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw
3220    int32_t jan1Start = handleComputeMonthStart(yearWoy, 0, FALSE);
3221    int32_t nextJan1Start = handleComputeMonthStart(yearWoy+1, 0, FALSE); // next year's Jan1 start
3222
3223    // At this point julianDay is the 0-based day BEFORE the first day of
3224    // January 1, year 1 of the given calendar.  If julianDay == 0, it
3225    // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
3226    // or Gregorian). (or it is before the month we are in, if useMonth is True)
3227
3228    // At this point we need to process the WEEK_OF_MONTH or
3229    // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
3230    // First, perform initial shared computations.  These locate the
3231    // first week of the period.
3232
3233    // Get the 0-based localized DOW of day one of the month or year.
3234    // Valid range 0..6.
3235    int32_t first = julianDayToDayOfWeek(jan1Start + 1) - firstDayOfWeek;
3236    if (first < 0) {
3237        first += 7;
3238    }
3239    int32_t nextFirst = julianDayToDayOfWeek(nextJan1Start + 1) - firstDayOfWeek;
3240    if (nextFirst < 0) {
3241        nextFirst += 7;
3242    }
3243
3244    int32_t minDays = getMinimalDaysInFirstWeek();
3245    UBool jan1InPrevYear = FALSE;  // January 1st in the year of WOY is the 1st week?  (i.e. first week is < minimal )
3246    //UBool nextJan1InPrevYear = FALSE; // January 1st of Year of WOY + 1 is in the first week?
3247
3248    if((7 - first) < minDays) {
3249        jan1InPrevYear = TRUE;
3250    }
3251
3252    //   if((7 - nextFirst) < minDays) {
3253    //     nextJan1InPrevYear = TRUE;
3254    //   }
3255
3256    switch(bestField) {
3257    case UCAL_WEEK_OF_YEAR:
3258        if(woy == 1) {
3259            if(jan1InPrevYear == TRUE) {
3260                // the first week of January is in the previous year
3261                // therefore WOY1 is always solidly within yearWoy
3262                return yearWoy;
3263            } else {
3264                // First WOY is split between two years
3265                if( dowLocal < first) { // we are prior to Jan 1
3266                    return yearWoy-1; // previous year
3267                } else {
3268                    return yearWoy; // in this year
3269                }
3270            }
3271        } else if(woy >= getLeastMaximum(bestField)) {
3272            // we _might_ be in the last week..
3273            int32_t jd =  // Calculate JD of our target day:
3274                jan1Start +  // JD of Jan 1
3275                (7-first) + //  days in the first week (Jan 1.. )
3276                (woy-1)*7 + // add the weeks of the year
3277                dowLocal;   // the local dow (0..6) of last week
3278            if(jan1InPrevYear==FALSE) {
3279                jd -= 7; // woy already includes Jan 1's week.
3280            }
3281
3282            if( (jd+1) >= nextJan1Start ) {
3283                // we are in week 52 or 53 etc. - actual year is yearWoy+1
3284                return yearWoy+1;
3285            } else {
3286                // still in yearWoy;
3287                return yearWoy;
3288            }
3289        } else {
3290            // we're not possibly in the last week -must be ywoy
3291            return yearWoy;
3292        }
3293
3294    case UCAL_DATE:
3295        if((internalGet(UCAL_MONTH)==0) &&
3296            (woy >= getLeastMaximum(UCAL_WEEK_OF_YEAR))) {
3297                return yearWoy+1; // month 0, late woy = in the next year
3298            } else if(woy==1) {
3299                //if(nextJan1InPrevYear) {
3300                if(internalGet(UCAL_MONTH)==0) {
3301                    return yearWoy;
3302                } else {
3303                    return yearWoy-1;
3304                }
3305                //}
3306            }
3307
3308            //(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow  */ ) {
3309            //within 1st week and in this month..
3310            //return yearWoy+1;
3311            return yearWoy;
3312
3313    default: // assume the year is appropriate
3314        return yearWoy;
3315    }
3316}
3317
3318int32_t Calendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const
3319{
3320    return handleComputeMonthStart(extendedYear, month+1, TRUE) -
3321        handleComputeMonthStart(extendedYear, month, TRUE);
3322}
3323
3324int32_t Calendar::handleGetYearLength(int32_t eyear) const  {
3325    return handleComputeMonthStart(eyear+1, 0, FALSE) -
3326        handleComputeMonthStart(eyear, 0, FALSE);
3327}
3328
3329int32_t
3330Calendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const
3331{
3332    int32_t result;
3333    switch (field) {
3334    case UCAL_DATE:
3335        {
3336            if(U_FAILURE(status)) return 0;
3337            Calendar *cal = clone();
3338            if(!cal) { status = U_MEMORY_ALLOCATION_ERROR; return 0; }
3339            cal->setLenient(TRUE);
3340            cal->prepareGetActual(field,FALSE,status);
3341            result = handleGetMonthLength(cal->get(UCAL_EXTENDED_YEAR, status), cal->get(UCAL_MONTH, status));
3342            delete cal;
3343        }
3344        break;
3345
3346    case UCAL_DAY_OF_YEAR:
3347        {
3348            if(U_FAILURE(status)) return 0;
3349            Calendar *cal = clone();
3350            if(!cal) { status = U_MEMORY_ALLOCATION_ERROR; return 0; }
3351            cal->setLenient(TRUE);
3352            cal->prepareGetActual(field,FALSE,status);
3353            result = handleGetYearLength(cal->get(UCAL_EXTENDED_YEAR, status));
3354            delete cal;
3355        }
3356        break;
3357
3358    case UCAL_DAY_OF_WEEK:
3359    case UCAL_AM_PM:
3360    case UCAL_HOUR:
3361    case UCAL_HOUR_OF_DAY:
3362    case UCAL_MINUTE:
3363    case UCAL_SECOND:
3364    case UCAL_MILLISECOND:
3365    case UCAL_ZONE_OFFSET:
3366    case UCAL_DST_OFFSET:
3367    case UCAL_DOW_LOCAL:
3368    case UCAL_JULIAN_DAY:
3369    case UCAL_MILLISECONDS_IN_DAY:
3370        // These fields all have fixed minima/maxima
3371        result = getMaximum(field);
3372        break;
3373
3374    default:
3375        // For all other fields, do it the hard way....
3376        result = getActualHelper(field, getLeastMaximum(field), getMaximum(field),status);
3377        break;
3378    }
3379    return result;
3380}
3381
3382
3383/**
3384* Prepare this calendar for computing the actual minimum or maximum.
3385* This method modifies this calendar's fields; it is called on a
3386* temporary calendar.
3387*
3388* <p>Rationale: The semantics of getActualXxx() is to return the
3389* maximum or minimum value that the given field can take, taking into
3390* account other relevant fields.  In general these other fields are
3391* larger fields.  For example, when computing the actual maximum
3392* DATE, the current value of DATE itself is ignored,
3393* as is the value of any field smaller.
3394*
3395* <p>The time fields all have fixed minima and maxima, so we don't
3396* need to worry about them.  This also lets us set the
3397* MILLISECONDS_IN_DAY to zero to erase any effects the time fields
3398* might have when computing date fields.
3399*
3400* <p>DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and
3401* WEEK_OF_YEAR fields to ensure that they are computed correctly.
3402* @internal
3403*/
3404void Calendar::prepareGetActual(UCalendarDateFields field, UBool isMinimum, UErrorCode &status)
3405{
3406    set(UCAL_MILLISECONDS_IN_DAY, 0);
3407
3408    switch (field) {
3409    case UCAL_YEAR:
3410    case UCAL_EXTENDED_YEAR:
3411        set(UCAL_DAY_OF_YEAR, getGreatestMinimum(UCAL_DAY_OF_YEAR));
3412        break;
3413
3414    case UCAL_YEAR_WOY:
3415        set(UCAL_WEEK_OF_YEAR, getGreatestMinimum(UCAL_WEEK_OF_YEAR));
3416
3417    case UCAL_MONTH:
3418        set(UCAL_DATE, getGreatestMinimum(UCAL_DATE));
3419        break;
3420
3421    case UCAL_DAY_OF_WEEK_IN_MONTH:
3422        // For dowim, the maximum occurs for the DOW of the first of the
3423        // month.
3424        set(UCAL_DATE, 1);
3425        set(UCAL_DAY_OF_WEEK, get(UCAL_DAY_OF_WEEK, status)); // Make this user set
3426        break;
3427
3428    case UCAL_WEEK_OF_MONTH:
3429    case UCAL_WEEK_OF_YEAR:
3430        // If we're counting weeks, set the day of the week to either the
3431        // first or last localized DOW.  We know the last week of a month
3432        // or year will contain the first day of the week, and that the
3433        // first week will contain the last DOW.
3434        {
3435            int32_t dow = fFirstDayOfWeek;
3436            if (isMinimum) {
3437                dow = (dow + 6) % 7; // set to last DOW
3438                if (dow < UCAL_SUNDAY) {
3439                    dow += 7;
3440                }
3441            }
3442#if defined (U_DEBUG_CAL)
3443            fprintf(stderr, "prepareGetActualHelper(WOM/WOY) - dow=%d\n", dow);
3444#endif
3445            set(UCAL_DAY_OF_WEEK, dow);
3446        }
3447        break;
3448    default:
3449        break;
3450    }
3451
3452    // Do this last to give it the newest time stamp
3453    set(field, getGreatestMinimum(field));
3454}
3455
3456int32_t Calendar::getActualHelper(UCalendarDateFields field, int32_t startValue, int32_t endValue, UErrorCode &status) const
3457{
3458#if defined (U_DEBUG_CAL)
3459    fprintf(stderr, "getActualHelper(%d,%d .. %d, %s)\n", field, startValue, endValue, u_errorName(status));
3460#endif
3461    if (startValue == endValue) {
3462        // if we know that the maximum value is always the same, just return it
3463        return startValue;
3464    }
3465
3466    int32_t delta = (endValue > startValue) ? 1 : -1;
3467
3468    // clone the calendar so we don't mess with the real one, and set it to
3469    // accept anything for the field values
3470    if(U_FAILURE(status)) return startValue;
3471    Calendar *work = clone();
3472    if(!work) { status = U_MEMORY_ALLOCATION_ERROR; return startValue; }
3473
3474    // need to resolve time here, otherwise, fields set for actual limit
3475    // may cause conflict with fields previously set (but not yet resolved).
3476    work->complete(status);
3477
3478    work->setLenient(TRUE);
3479    work->prepareGetActual(field, delta < 0, status);
3480
3481    // now try each value from the start to the end one by one until
3482    // we get a value that normalizes to another value.  The last value that
3483    // normalizes to itself is the actual maximum for the current date
3484    work->set(field, startValue);
3485
3486    // prepareGetActual sets the first day of week in the same week with
3487    // the first day of a month.  Unlike WEEK_OF_YEAR, week number for the
3488    // week which contains days from both previous and current month is
3489    // not unique.  For example, last several days in the previous month
3490    // is week 5, and the rest of week is week 1.
3491    int32_t result = startValue;
3492    if ((work->get(field, status) != startValue
3493         && field != UCAL_WEEK_OF_MONTH && delta > 0 ) || U_FAILURE(status)) {
3494#if defined (U_DEBUG_CAL)
3495        fprintf(stderr, "getActualHelper(fld %d) - got  %d (not %d) - %s\n", field, work->get(field,status), startValue, u_errorName(status));
3496#endif
3497    } else {
3498        do {
3499            startValue += delta;
3500            work->add(field, delta, status);
3501            if (work->get(field, status) != startValue || U_FAILURE(status)) {
3502#if defined (U_DEBUG_CAL)
3503                fprintf(stderr, "getActualHelper(fld %d) - got  %d (not %d), BREAK - %s\n", field, work->get(field,status), startValue, u_errorName(status));
3504#endif
3505                break;
3506            }
3507            result = startValue;
3508        } while (startValue != endValue);
3509    }
3510    delete work;
3511#if defined (U_DEBUG_CAL)
3512    fprintf(stderr, "getActualHelper(%d) = %d\n", field, result);
3513#endif
3514    return result;
3515}
3516
3517
3518
3519
3520// -------------------------------------
3521
3522void
3523Calendar::setWeekData(const Locale& desiredLocale, const char *type, UErrorCode& status)
3524{
3525
3526    if (U_FAILURE(status)) return;
3527
3528    fFirstDayOfWeek = UCAL_SUNDAY;
3529    fMinimalDaysInFirstWeek = 1;
3530    fWeekendOnset = UCAL_SATURDAY;
3531    fWeekendOnsetMillis = 0;
3532    fWeekendCease = UCAL_SUNDAY;
3533    fWeekendCeaseMillis = 86400000; // 24*60*60*1000
3534
3535    // Since week and weekend data is territory based instead of language based,
3536    // we may need to tweak the locale that we are using to try to get the appropriate
3537    // values, using the following logic:
3538    // 1). If the locale has a language but no territory, use the territory as defined by
3539    //     the likely subtags.
3540    // 2). If the locale has a script designation then we ignore it,
3541    //     then remove it ( i.e. "en_Latn_US" becomes "en_US" )
3542
3543    char minLocaleID[ULOC_FULLNAME_CAPACITY] = { 0 };
3544    UErrorCode myStatus = U_ZERO_ERROR;
3545
3546    uloc_minimizeSubtags(desiredLocale.getName(),minLocaleID,ULOC_FULLNAME_CAPACITY,&myStatus);
3547    Locale min = Locale::createFromName(minLocaleID);
3548    Locale useLocale;
3549    if ( uprv_strlen(desiredLocale.getCountry()) == 0 ||
3550         (uprv_strlen(desiredLocale.getScript()) > 0 && uprv_strlen(min.getScript()) == 0) ) {
3551        char maxLocaleID[ULOC_FULLNAME_CAPACITY] = { 0 };
3552        myStatus = U_ZERO_ERROR;
3553        uloc_addLikelySubtags(desiredLocale.getName(),maxLocaleID,ULOC_FULLNAME_CAPACITY,&myStatus);
3554        Locale max = Locale::createFromName(maxLocaleID);
3555        useLocale = Locale(max.getLanguage(),max.getCountry());
3556    } else {
3557        useLocale = Locale(desiredLocale);
3558    }
3559
3560    /* The code here is somewhat of a hack, since week data and weekend data aren't really tied to
3561       a specific calendar, they aren't truly locale data.  But this is the only place where valid and
3562       actual locale can be set, so we take a shot at it here by loading a representative resource
3563       from the calendar data.  The code used to use the dateTimeElements resource to get first day
3564       of week data, but this was moved to supplemental data under ticket 7755. (JCE) */
3565
3566    CalendarData calData(useLocale,type,status);
3567    UResourceBundle *monthNames = calData.getByKey(gMonthNames,status);
3568    if (U_SUCCESS(status)) {
3569        U_LOCALE_BASED(locBased,*this);
3570        locBased.setLocaleIDs(ures_getLocaleByType(monthNames, ULOC_VALID_LOCALE, &status),
3571                              ures_getLocaleByType(monthNames, ULOC_ACTUAL_LOCALE, &status));
3572    } else {
3573        status = U_USING_FALLBACK_WARNING;
3574        return;
3575    }
3576
3577
3578    // Read week data values from supplementalData week data
3579    UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", &status);
3580    ures_getByKey(rb, "weekData", rb, &status);
3581    UResourceBundle *weekData = ures_getByKey(rb, useLocale.getCountry(), NULL, &status);
3582    if (status == U_MISSING_RESOURCE_ERROR && rb != NULL) {
3583        status = U_ZERO_ERROR;
3584        weekData = ures_getByKey(rb, "001", NULL, &status);
3585    }
3586
3587    if (U_FAILURE(status)) {
3588#if defined (U_DEBUG_CALDATA)
3589        fprintf(stderr, " Failure loading weekData from supplemental = %s\n", u_errorName(status));
3590#endif
3591        status = U_USING_FALLBACK_WARNING;
3592    } else {
3593        int32_t arrLen;
3594        const int32_t *weekDataArr = ures_getIntVector(weekData,&arrLen,&status);
3595        if( U_SUCCESS(status) && arrLen == 6
3596                && 1 <= weekDataArr[0] && weekDataArr[0] <= 7
3597                && 1 <= weekDataArr[1] && weekDataArr[1] <= 7
3598                && 1 <= weekDataArr[2] && weekDataArr[2] <= 7
3599                && 1 <= weekDataArr[4] && weekDataArr[4] <= 7) {
3600            fFirstDayOfWeek = (UCalendarDaysOfWeek)weekDataArr[0];
3601            fMinimalDaysInFirstWeek = (uint8_t)weekDataArr[1];
3602            fWeekendOnset = (UCalendarDaysOfWeek)weekDataArr[2];
3603            fWeekendOnsetMillis = weekDataArr[3];
3604            fWeekendCease = (UCalendarDaysOfWeek)weekDataArr[4];
3605            fWeekendCeaseMillis = weekDataArr[5];
3606        } else {
3607            status = U_INVALID_FORMAT_ERROR;
3608        }
3609    }
3610    ures_close(weekData);
3611    ures_close(rb);
3612}
3613
3614/**
3615* Recompute the time and update the status fields isTimeSet
3616* and areFieldsSet.  Callers should check isTimeSet and only
3617* call this method if isTimeSet is false.
3618*/
3619void
3620Calendar::updateTime(UErrorCode& status)
3621{
3622    computeTime(status);
3623    if(U_FAILURE(status))
3624        return;
3625
3626    // If we are lenient, we need to recompute the fields to normalize
3627    // the values.  Also, if we haven't set all the fields yet (i.e.,
3628    // in a newly-created object), we need to fill in the fields. [LIU]
3629    if (isLenient() || ! fAreAllFieldsSet)
3630        fAreFieldsSet = FALSE;
3631
3632    fIsTimeSet = TRUE;
3633    fAreFieldsVirtuallySet = FALSE;
3634}
3635
3636Locale
3637Calendar::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
3638    U_LOCALE_BASED(locBased, *this);
3639    return locBased.getLocale(type, status);
3640}
3641
3642const char *
3643Calendar::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const {
3644    U_LOCALE_BASED(locBased, *this);
3645    return locBased.getLocaleID(type, status);
3646}
3647
3648void
3649Calendar::recalculateStamp() {
3650    int32_t index;
3651    int32_t currentValue;
3652    int32_t j, i;
3653
3654    fNextStamp = 1;
3655
3656    for (j = 0; j < UCAL_FIELD_COUNT; j++) {
3657        currentValue = STAMP_MAX;
3658        index = -1;
3659        for (i = 0; i < UCAL_FIELD_COUNT; i++) {
3660            if (fStamp[i] > fNextStamp && fStamp[i] < currentValue) {
3661                currentValue = fStamp[i];
3662                index = i;
3663            }
3664        }
3665
3666        if (index >= 0) {
3667            fStamp[index] = ++fNextStamp;
3668        } else {
3669            break;
3670        }
3671    }
3672    fNextStamp++;
3673}
3674
3675// Deprecated function. This doesn't need to be inline.
3676void
3677Calendar::internalSet(EDateFields field, int32_t value)
3678{
3679    internalSet((UCalendarDateFields) field, value);
3680}
3681
3682BasicTimeZone*
3683Calendar::getBasicTimeZone(void) const {
3684    if (dynamic_cast<const OlsonTimeZone *>(fZone) != NULL
3685        || dynamic_cast<const SimpleTimeZone *>(fZone) != NULL
3686        || dynamic_cast<const RuleBasedTimeZone *>(fZone) != NULL
3687        || dynamic_cast<const VTimeZone *>(fZone) != NULL) {
3688        return (BasicTimeZone*)fZone;
3689    }
3690    return NULL;
3691}
3692
3693U_NAMESPACE_END
3694
3695#endif /* #if !UCONFIG_NO_FORMATTING */
3696
3697
3698//eof
3699
3700