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