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