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