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