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