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