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