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