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