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