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