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