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