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