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