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