tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

calendar.cpp (150159B)


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