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