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