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