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