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