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