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