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