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