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