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