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