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