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