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