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