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