1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/calendar.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,3699 @@ 1.4 +/* 1.5 +******************************************************************************* 1.6 +* Copyright (C) 1997-2013, International Business Machines Corporation and * 1.7 +* others. All Rights Reserved. * 1.8 +******************************************************************************* 1.9 +* 1.10 +* File CALENDAR.CPP 1.11 +* 1.12 +* Modification History: 1.13 +* 1.14 +* Date Name Description 1.15 +* 02/03/97 clhuang Creation. 1.16 +* 04/22/97 aliu Cleaned up, fixed memory leak, made 1.17 +* setWeekCountData() more robust. 1.18 +* Moved platform code to TPlatformUtilities. 1.19 +* 05/01/97 aliu Made equals(), before(), after() arguments const. 1.20 +* 05/20/97 aliu Changed logic of when to compute fields and time 1.21 +* to fix bugs. 1.22 +* 08/12/97 aliu Added equivalentTo. Misc other fixes. 1.23 +* 07/28/98 stephen Sync up with JDK 1.2 1.24 +* 09/02/98 stephen Sync with JDK 1.2 8/31 build (getActualMin/Max) 1.25 +* 03/17/99 stephen Changed adoptTimeZone() - now fAreFieldsSet is 1.26 +* set to FALSE to force update of time. 1.27 +******************************************************************************* 1.28 +*/ 1.29 + 1.30 +#include "utypeinfo.h" // for 'typeid' to work 1.31 + 1.32 +#include "unicode/utypes.h" 1.33 + 1.34 +#if !UCONFIG_NO_FORMATTING 1.35 + 1.36 +#include "unicode/gregocal.h" 1.37 +#include "unicode/basictz.h" 1.38 +#include "unicode/simpletz.h" 1.39 +#include "unicode/rbtz.h" 1.40 +#include "unicode/vtzone.h" 1.41 +#include "gregoimp.h" 1.42 +#include "buddhcal.h" 1.43 +#include "taiwncal.h" 1.44 +#include "japancal.h" 1.45 +#include "islamcal.h" 1.46 +#include "hebrwcal.h" 1.47 +#include "persncal.h" 1.48 +#include "indiancal.h" 1.49 +#include "chnsecal.h" 1.50 +#include "coptccal.h" 1.51 +#include "dangical.h" 1.52 +#include "ethpccal.h" 1.53 +#include "unicode/calendar.h" 1.54 +#include "cpputils.h" 1.55 +#include "servloc.h" 1.56 +#include "ucln_in.h" 1.57 +#include "cstring.h" 1.58 +#include "locbased.h" 1.59 +#include "uresimp.h" 1.60 +#include "ustrenum.h" 1.61 +#include "uassert.h" 1.62 +#include "olsontz.h" 1.63 + 1.64 +#if !UCONFIG_NO_SERVICE 1.65 +static icu::ICULocaleService* gService = NULL; 1.66 +static icu::UInitOnce gServiceInitOnce = U_INITONCE_INITIALIZER; 1.67 +#endif 1.68 + 1.69 +// INTERNAL - for cleanup 1.70 + 1.71 +U_CDECL_BEGIN 1.72 +static UBool calendar_cleanup(void) { 1.73 +#if !UCONFIG_NO_SERVICE 1.74 + if (gService) { 1.75 + delete gService; 1.76 + gService = NULL; 1.77 + } 1.78 + gServiceInitOnce.reset(); 1.79 +#endif 1.80 + return TRUE; 1.81 +} 1.82 +U_CDECL_END 1.83 + 1.84 +// ------------------------------------------ 1.85 +// 1.86 +// Registration 1.87 +// 1.88 +//------------------------------------------- 1.89 +//#define U_DEBUG_CALSVC 1 1.90 +// 1.91 + 1.92 +#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) 1.93 + 1.94 +/** 1.95 + * fldName was removed as a duplicate implementation. 1.96 + * use udbg_ services instead, 1.97 + * which depend on include files and library from ../tools/toolutil, the following circular link: 1.98 + * CPPFLAGS+=-I$(top_srcdir)/tools/toolutil 1.99 + * LIBS+=$(LIBICUTOOLUTIL) 1.100 + */ 1.101 +#include "udbgutil.h" 1.102 +#include <stdio.h> 1.103 + 1.104 +/** 1.105 +* convert a UCalendarDateFields into a string - for debugging 1.106 +* @param f field enum 1.107 +* @return static string to the field name 1.108 +* @internal 1.109 +*/ 1.110 + 1.111 +const char* fldName(UCalendarDateFields f) { 1.112 + return udbg_enumName(UDBG_UCalendarDateFields, (int32_t)f); 1.113 +} 1.114 + 1.115 +#if UCAL_DEBUG_DUMP 1.116 +// from CalendarTest::calToStr - but doesn't modify contents. 1.117 +void ucal_dump(const Calendar &cal) { 1.118 + cal.dump(); 1.119 +} 1.120 + 1.121 +void Calendar::dump() const { 1.122 + int i; 1.123 + fprintf(stderr, "@calendar=%s, timeset=%c, fieldset=%c, allfields=%c, virtualset=%c, t=%.2f", 1.124 + getType(), fIsTimeSet?'y':'n', fAreFieldsSet?'y':'n', fAreAllFieldsSet?'y':'n', 1.125 + fAreFieldsVirtuallySet?'y':'n', 1.126 + fTime); 1.127 + 1.128 + // can add more things here: DST, zone, etc. 1.129 + fprintf(stderr, "\n"); 1.130 + for(i = 0;i<UCAL_FIELD_COUNT;i++) { 1.131 + int n; 1.132 + const char *f = fldName((UCalendarDateFields)i); 1.133 + fprintf(stderr, " %25s: %-11ld", f, fFields[i]); 1.134 + if(fStamp[i] == kUnset) { 1.135 + fprintf(stderr, " (unset) "); 1.136 + } else if(fStamp[i] == kInternallySet) { 1.137 + fprintf(stderr, " (internally set) "); 1.138 + //} else if(fStamp[i] == kInternalDefault) { 1.139 + // fprintf(stderr, " (internal default) "); 1.140 + } else { 1.141 + fprintf(stderr, " %%%d ", fStamp[i]); 1.142 + } 1.143 + fprintf(stderr, "\n"); 1.144 + 1.145 + } 1.146 +} 1.147 + 1.148 +U_CFUNC void ucal_dump(UCalendar* cal) { 1.149 + ucal_dump( *((Calendar*)cal) ); 1.150 +} 1.151 +#endif 1.152 + 1.153 +#endif 1.154 + 1.155 +/* Max value for stamp allowable before recalculation */ 1.156 +#define STAMP_MAX 10000 1.157 + 1.158 +static const char * const gCalTypes[] = { 1.159 + "gregorian", 1.160 + "japanese", 1.161 + "buddhist", 1.162 + "roc", 1.163 + "persian", 1.164 + "islamic-civil", 1.165 + "islamic", 1.166 + "hebrew", 1.167 + "chinese", 1.168 + "indian", 1.169 + "coptic", 1.170 + "ethiopic", 1.171 + "ethiopic-amete-alem", 1.172 + "iso8601", 1.173 + "dangi", 1.174 + "islamic-umalqura", 1.175 + "islamic-tbla", 1.176 + "islamic-rgsa", 1.177 + NULL 1.178 +}; 1.179 + 1.180 +// Must be in the order of gCalTypes above 1.181 +typedef enum ECalType { 1.182 + CALTYPE_UNKNOWN = -1, 1.183 + CALTYPE_GREGORIAN = 0, 1.184 + CALTYPE_JAPANESE, 1.185 + CALTYPE_BUDDHIST, 1.186 + CALTYPE_ROC, 1.187 + CALTYPE_PERSIAN, 1.188 + CALTYPE_ISLAMIC_CIVIL, 1.189 + CALTYPE_ISLAMIC, 1.190 + CALTYPE_HEBREW, 1.191 + CALTYPE_CHINESE, 1.192 + CALTYPE_INDIAN, 1.193 + CALTYPE_COPTIC, 1.194 + CALTYPE_ETHIOPIC, 1.195 + CALTYPE_ETHIOPIC_AMETE_ALEM, 1.196 + CALTYPE_ISO8601, 1.197 + CALTYPE_DANGI, 1.198 + CALTYPE_ISLAMIC_UMALQURA, 1.199 + CALTYPE_ISLAMIC_TBLA, 1.200 + CALTYPE_ISLAMIC_RGSA 1.201 +} ECalType; 1.202 + 1.203 +U_NAMESPACE_BEGIN 1.204 + 1.205 +static ECalType getCalendarType(const char *s) { 1.206 + for (int i = 0; gCalTypes[i] != NULL; i++) { 1.207 + if (uprv_stricmp(s, gCalTypes[i]) == 0) { 1.208 + return (ECalType)i; 1.209 + } 1.210 + } 1.211 + return CALTYPE_UNKNOWN; 1.212 +} 1.213 + 1.214 +static UBool isStandardSupportedKeyword(const char *keyword, UErrorCode& status) { 1.215 + if(U_FAILURE(status)) { 1.216 + return FALSE; 1.217 + } 1.218 + ECalType calType = getCalendarType(keyword); 1.219 + return (calType != CALTYPE_UNKNOWN); 1.220 +} 1.221 + 1.222 +static void getCalendarKeyword(const UnicodeString &id, char *targetBuffer, int32_t targetBufferSize) { 1.223 + UnicodeString calendarKeyword = UNICODE_STRING_SIMPLE("calendar="); 1.224 + int32_t calKeyLen = calendarKeyword.length(); 1.225 + int32_t keyLen = 0; 1.226 + 1.227 + int32_t keywordIdx = id.indexOf((UChar)0x003D); /* '=' */ 1.228 + if (id[0] == 0x40/*'@'*/ 1.229 + && id.compareBetween(1, keywordIdx+1, calendarKeyword, 0, calKeyLen) == 0) 1.230 + { 1.231 + keyLen = id.extract(keywordIdx+1, id.length(), targetBuffer, targetBufferSize, US_INV); 1.232 + } 1.233 + targetBuffer[keyLen] = 0; 1.234 +} 1.235 + 1.236 +static ECalType getCalendarTypeForLocale(const char *locid) { 1.237 + UErrorCode status = U_ZERO_ERROR; 1.238 + ECalType calType = CALTYPE_UNKNOWN; 1.239 + 1.240 + //TODO: ULOC_FULL_NAME is out of date and too small.. 1.241 + char canonicalName[256]; 1.242 + 1.243 + // canonicalize, so grandfathered variant will be transformed to keywords 1.244 + // e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese 1.245 + int32_t canonicalLen = uloc_canonicalize(locid, canonicalName, sizeof(canonicalName) - 1, &status); 1.246 + if (U_FAILURE(status)) { 1.247 + return CALTYPE_GREGORIAN; 1.248 + } 1.249 + canonicalName[canonicalLen] = 0; // terminate 1.250 + 1.251 + char calTypeBuf[32]; 1.252 + int32_t calTypeBufLen; 1.253 + 1.254 + calTypeBufLen = uloc_getKeywordValue(canonicalName, "calendar", calTypeBuf, sizeof(calTypeBuf) - 1, &status); 1.255 + if (U_SUCCESS(status)) { 1.256 + calTypeBuf[calTypeBufLen] = 0; 1.257 + calType = getCalendarType(calTypeBuf); 1.258 + if (calType != CALTYPE_UNKNOWN) { 1.259 + return calType; 1.260 + } 1.261 + } 1.262 + status = U_ZERO_ERROR; 1.263 + 1.264 + // when calendar keyword is not available or not supported, read supplementalData 1.265 + // to get the default calendar type for the locale's region 1.266 + char region[ULOC_COUNTRY_CAPACITY]; 1.267 + int32_t regionLen = 0; 1.268 + regionLen = uloc_getCountry(canonicalName, region, sizeof(region) - 1, &status); 1.269 + if (regionLen == 0) { 1.270 + char fullLoc[256]; 1.271 + uloc_addLikelySubtags(locid, fullLoc, sizeof(fullLoc) - 1, &status); 1.272 + regionLen = uloc_getCountry(fullLoc, region, sizeof(region) - 1, &status); 1.273 + } 1.274 + if (U_FAILURE(status)) { 1.275 + return CALTYPE_GREGORIAN; 1.276 + } 1.277 + region[regionLen] = 0; 1.278 + 1.279 + // Read preferred calendar values from supplementalData calendarPreference 1.280 + UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", &status); 1.281 + ures_getByKey(rb, "calendarPreferenceData", rb, &status); 1.282 + UResourceBundle *order = ures_getByKey(rb, region, NULL, &status); 1.283 + if (status == U_MISSING_RESOURCE_ERROR && rb != NULL) { 1.284 + status = U_ZERO_ERROR; 1.285 + order = ures_getByKey(rb, "001", NULL, &status); 1.286 + } 1.287 + 1.288 + calTypeBuf[0] = 0; 1.289 + if (U_SUCCESS(status) && order != NULL) { 1.290 + // the first calender type is the default for the region 1.291 + int32_t len = 0; 1.292 + const UChar *uCalType = ures_getStringByIndex(order, 0, &len, &status); 1.293 + if (len < (int32_t)sizeof(calTypeBuf)) { 1.294 + u_UCharsToChars(uCalType, calTypeBuf, len); 1.295 + *(calTypeBuf + len) = 0; // terminate; 1.296 + calType = getCalendarType(calTypeBuf); 1.297 + } 1.298 + } 1.299 + 1.300 + ures_close(order); 1.301 + ures_close(rb); 1.302 + 1.303 + if (calType == CALTYPE_UNKNOWN) { 1.304 + // final fallback 1.305 + calType = CALTYPE_GREGORIAN; 1.306 + } 1.307 + return calType; 1.308 +} 1.309 + 1.310 +static Calendar *createStandardCalendar(ECalType calType, const Locale &loc, UErrorCode& status) { 1.311 + Calendar *cal = NULL; 1.312 + 1.313 + switch (calType) { 1.314 + case CALTYPE_GREGORIAN: 1.315 + cal = new GregorianCalendar(loc, status); 1.316 + break; 1.317 + case CALTYPE_JAPANESE: 1.318 + cal = new JapaneseCalendar(loc, status); 1.319 + break; 1.320 + case CALTYPE_BUDDHIST: 1.321 + cal = new BuddhistCalendar(loc, status); 1.322 + break; 1.323 + case CALTYPE_ROC: 1.324 + cal = new TaiwanCalendar(loc, status); 1.325 + break; 1.326 + case CALTYPE_PERSIAN: 1.327 + cal = new PersianCalendar(loc, status); 1.328 + break; 1.329 + case CALTYPE_ISLAMIC_TBLA: 1.330 + cal = new IslamicCalendar(loc, status, IslamicCalendar::TBLA); 1.331 + break; 1.332 + case CALTYPE_ISLAMIC_CIVIL: 1.333 + cal = new IslamicCalendar(loc, status, IslamicCalendar::CIVIL); 1.334 + break; 1.335 + case CALTYPE_ISLAMIC_RGSA: 1.336 + // default any region specific not handled individually to islamic 1.337 + case CALTYPE_ISLAMIC: 1.338 + cal = new IslamicCalendar(loc, status, IslamicCalendar::ASTRONOMICAL); 1.339 + break; 1.340 + case CALTYPE_ISLAMIC_UMALQURA: 1.341 + cal = new IslamicCalendar(loc, status, IslamicCalendar::UMALQURA); 1.342 + break; 1.343 + case CALTYPE_HEBREW: 1.344 + cal = new HebrewCalendar(loc, status); 1.345 + break; 1.346 + case CALTYPE_CHINESE: 1.347 + cal = new ChineseCalendar(loc, status); 1.348 + break; 1.349 + case CALTYPE_INDIAN: 1.350 + cal = new IndianCalendar(loc, status); 1.351 + break; 1.352 + case CALTYPE_COPTIC: 1.353 + cal = new CopticCalendar(loc, status); 1.354 + break; 1.355 + case CALTYPE_ETHIOPIC: 1.356 + cal = new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_MIHRET_ERA); 1.357 + break; 1.358 + case CALTYPE_ETHIOPIC_AMETE_ALEM: 1.359 + cal = new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_ALEM_ERA); 1.360 + break; 1.361 + case CALTYPE_ISO8601: 1.362 + cal = new GregorianCalendar(loc, status); 1.363 + cal->setFirstDayOfWeek(UCAL_MONDAY); 1.364 + cal->setMinimalDaysInFirstWeek(4); 1.365 + break; 1.366 + case CALTYPE_DANGI: 1.367 + cal = new DangiCalendar(loc, status); 1.368 + break; 1.369 + default: 1.370 + status = U_UNSUPPORTED_ERROR; 1.371 + } 1.372 + return cal; 1.373 +} 1.374 + 1.375 + 1.376 +#if !UCONFIG_NO_SERVICE 1.377 + 1.378 +// ------------------------------------- 1.379 + 1.380 +/** 1.381 +* a Calendar Factory which creates the "basic" calendar types, that is, those 1.382 +* shipped with ICU. 1.383 +*/ 1.384 +class BasicCalendarFactory : public LocaleKeyFactory { 1.385 +public: 1.386 + /** 1.387 + * @param calendarType static const string (caller owns storage - will be aliased) to calendar type 1.388 + */ 1.389 + BasicCalendarFactory() 1.390 + : LocaleKeyFactory(LocaleKeyFactory::INVISIBLE) { } 1.391 + 1.392 + virtual ~BasicCalendarFactory(); 1.393 + 1.394 +protected: 1.395 + //virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const { 1.396 + // if(U_FAILURE(status)) { 1.397 + // return FALSE; 1.398 + // } 1.399 + // char keyword[ULOC_FULLNAME_CAPACITY]; 1.400 + // getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword)); 1.401 + // return isStandardSupportedKeyword(keyword, status); 1.402 + //} 1.403 + 1.404 + virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const 1.405 + { 1.406 + if (U_SUCCESS(status)) { 1.407 + for(int32_t i=0;gCalTypes[i] != NULL;i++) { 1.408 + UnicodeString id((UChar)0x40); /* '@' a variant character */ 1.409 + id.append(UNICODE_STRING_SIMPLE("calendar=")); 1.410 + id.append(UnicodeString(gCalTypes[i], -1, US_INV)); 1.411 + result.put(id, (void*)this, status); 1.412 + } 1.413 + } 1.414 + } 1.415 + 1.416 + virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const { 1.417 +#ifdef U_DEBUG_CALSVC 1.418 + if(dynamic_cast<const LocaleKey*>(&key) == NULL) { 1.419 + fprintf(stderr, "::create - not a LocaleKey!\n"); 1.420 + } 1.421 +#endif 1.422 + const LocaleKey& lkey = (LocaleKey&)key; 1.423 + Locale curLoc; // current locale 1.424 + Locale canLoc; // Canonical locale 1.425 + 1.426 + lkey.currentLocale(curLoc); 1.427 + lkey.canonicalLocale(canLoc); 1.428 + 1.429 + char keyword[ULOC_FULLNAME_CAPACITY]; 1.430 + UnicodeString str; 1.431 + 1.432 + key.currentID(str); 1.433 + getCalendarKeyword(str, keyword, (int32_t) sizeof(keyword)); 1.434 + 1.435 +#ifdef U_DEBUG_CALSVC 1.436 + fprintf(stderr, "BasicCalendarFactory::create() - cur %s, can %s\n", (const char*)curLoc.getName(), (const char*)canLoc.getName()); 1.437 +#endif 1.438 + 1.439 + if(!isStandardSupportedKeyword(keyword,status)) { // Do we handle this type? 1.440 +#ifdef U_DEBUG_CALSVC 1.441 + 1.442 + fprintf(stderr, "BasicCalendarFactory - not handling %s.[%s]\n", (const char*) curLoc.getName(), tmp ); 1.443 +#endif 1.444 + return NULL; 1.445 + } 1.446 + 1.447 + return createStandardCalendar(getCalendarType(keyword), canLoc, status); 1.448 + } 1.449 +}; 1.450 + 1.451 +BasicCalendarFactory::~BasicCalendarFactory() {} 1.452 + 1.453 +/** 1.454 +* A factory which looks up the DefaultCalendar resource to determine which class of calendar to use 1.455 +*/ 1.456 + 1.457 +class DefaultCalendarFactory : public ICUResourceBundleFactory { 1.458 +public: 1.459 + DefaultCalendarFactory() : ICUResourceBundleFactory() { } 1.460 + virtual ~DefaultCalendarFactory(); 1.461 +protected: 1.462 + virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const { 1.463 + 1.464 + LocaleKey &lkey = (LocaleKey&)key; 1.465 + Locale loc; 1.466 + lkey.currentLocale(loc); 1.467 + 1.468 + UnicodeString *ret = new UnicodeString(); 1.469 + if (ret == NULL) { 1.470 + status = U_MEMORY_ALLOCATION_ERROR; 1.471 + } else { 1.472 + ret->append((UChar)0x40); // '@' is a variant character 1.473 + ret->append(UNICODE_STRING("calendar=", 9)); 1.474 + ret->append(UnicodeString(gCalTypes[getCalendarTypeForLocale(loc.getName())], -1, US_INV)); 1.475 + } 1.476 + return ret; 1.477 + } 1.478 +}; 1.479 + 1.480 +DefaultCalendarFactory::~DefaultCalendarFactory() {} 1.481 + 1.482 +// ------------------------------------- 1.483 +class CalendarService : public ICULocaleService { 1.484 +public: 1.485 + CalendarService() 1.486 + : ICULocaleService(UNICODE_STRING_SIMPLE("Calendar")) 1.487 + { 1.488 + UErrorCode status = U_ZERO_ERROR; 1.489 + registerFactory(new DefaultCalendarFactory(), status); 1.490 + } 1.491 + 1.492 + virtual ~CalendarService(); 1.493 + 1.494 + virtual UObject* cloneInstance(UObject* instance) const { 1.495 + UnicodeString *s = dynamic_cast<UnicodeString *>(instance); 1.496 + if(s != NULL) { 1.497 + return s->clone(); 1.498 + } else { 1.499 +#ifdef U_DEBUG_CALSVC_F 1.500 + UErrorCode status2 = U_ZERO_ERROR; 1.501 + fprintf(stderr, "Cloning a %s calendar with tz=%ld\n", ((Calendar*)instance)->getType(), ((Calendar*)instance)->get(UCAL_ZONE_OFFSET, status2)); 1.502 +#endif 1.503 + return ((Calendar*)instance)->clone(); 1.504 + } 1.505 + } 1.506 + 1.507 + virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /*actualID*/, UErrorCode& status) const { 1.508 + LocaleKey& lkey = (LocaleKey&)key; 1.509 + //int32_t kind = lkey.kind(); 1.510 + 1.511 + Locale loc; 1.512 + lkey.canonicalLocale(loc); 1.513 + 1.514 +#ifdef U_DEBUG_CALSVC 1.515 + Locale loc2; 1.516 + lkey.currentLocale(loc2); 1.517 + fprintf(stderr, "CalSvc:handleDefault for currentLoc %s, canloc %s\n", (const char*)loc.getName(), (const char*)loc2.getName()); 1.518 +#endif 1.519 + Calendar *nc = new GregorianCalendar(loc, status); 1.520 + 1.521 +#ifdef U_DEBUG_CALSVC 1.522 + UErrorCode status2 = U_ZERO_ERROR; 1.523 + fprintf(stderr, "New default calendar has tz=%d\n", ((Calendar*)nc)->get(UCAL_ZONE_OFFSET, status2)); 1.524 +#endif 1.525 + return nc; 1.526 + } 1.527 + 1.528 + virtual UBool isDefault() const { 1.529 + return countFactories() == 1; 1.530 + } 1.531 +}; 1.532 + 1.533 +CalendarService::~CalendarService() {} 1.534 + 1.535 +// ------------------------------------- 1.536 + 1.537 +static inline UBool 1.538 +isCalendarServiceUsed() { 1.539 + return !gServiceInitOnce.isReset(); 1.540 +} 1.541 + 1.542 +// ------------------------------------- 1.543 + 1.544 +static void U_CALLCONV 1.545 +initCalendarService(UErrorCode &status) 1.546 +{ 1.547 +#ifdef U_DEBUG_CALSVC 1.548 + fprintf(stderr, "Spinning up Calendar Service\n"); 1.549 +#endif 1.550 + ucln_i18n_registerCleanup(UCLN_I18N_CALENDAR, calendar_cleanup); 1.551 + gService = new CalendarService(); 1.552 + if (gService == NULL) { 1.553 + status = U_MEMORY_ALLOCATION_ERROR; 1.554 + return; 1.555 + } 1.556 +#ifdef U_DEBUG_CALSVC 1.557 + fprintf(stderr, "Registering classes..\n"); 1.558 +#endif 1.559 + 1.560 + // Register all basic instances. 1.561 + gService->registerFactory(new BasicCalendarFactory(),status); 1.562 + 1.563 +#ifdef U_DEBUG_CALSVC 1.564 + fprintf(stderr, "Done..\n"); 1.565 +#endif 1.566 + 1.567 + if(U_FAILURE(status)) { 1.568 +#ifdef U_DEBUG_CALSVC 1.569 + fprintf(stderr, "err (%s) registering classes, deleting service.....\n", u_errorName(status)); 1.570 +#endif 1.571 + delete gService; 1.572 + gService = NULL; 1.573 + } 1.574 + } 1.575 + 1.576 +static ICULocaleService* 1.577 +getCalendarService(UErrorCode &status) 1.578 +{ 1.579 + umtx_initOnce(gServiceInitOnce, &initCalendarService, status); 1.580 + return gService; 1.581 +} 1.582 + 1.583 +URegistryKey Calendar::registerFactory(ICUServiceFactory* toAdopt, UErrorCode& status) 1.584 +{ 1.585 + return getCalendarService(status)->registerFactory(toAdopt, status); 1.586 +} 1.587 + 1.588 +UBool Calendar::unregister(URegistryKey key, UErrorCode& status) { 1.589 + return getCalendarService(status)->unregister(key, status); 1.590 +} 1.591 +#endif /* UCONFIG_NO_SERVICE */ 1.592 + 1.593 +// ------------------------------------- 1.594 + 1.595 +static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = { 1.596 + // Minimum Greatest min Least max Greatest max 1.597 + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // ERA 1.598 + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR 1.599 + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // MONTH 1.600 + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_YEAR 1.601 + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // WEEK_OF_MONTH 1.602 + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_MONTH 1.603 + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_YEAR 1.604 + { 1, 1, 7, 7 }, // DAY_OF_WEEK 1.605 + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // DAY_OF_WEEK_IN_MONTH 1.606 + { 0, 0, 1, 1 }, // AM_PM 1.607 + { 0, 0, 11, 11 }, // HOUR 1.608 + { 0, 0, 23, 23 }, // HOUR_OF_DAY 1.609 + { 0, 0, 59, 59 }, // MINUTE 1.610 + { 0, 0, 59, 59 }, // SECOND 1.611 + { 0, 0, 999, 999 }, // MILLISECOND 1.612 + {-12*kOneHour, -12*kOneHour, 12*kOneHour, 15*kOneHour }, // ZONE_OFFSET 1.613 + { 0, 0, 1*kOneHour, 1*kOneHour }, // DST_OFFSET 1.614 + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR_WOY 1.615 + { 1, 1, 7, 7 }, // DOW_LOCAL 1.616 + {/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // EXTENDED_YEAR 1.617 + { -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY 1.618 + { 0, 0, 24*kOneHour-1, 24*kOneHour-1 }, // MILLISECONDS_IN_DAY 1.619 + { 0, 0, 1, 1 }, // IS_LEAP_MONTH 1.620 +}; 1.621 + 1.622 +// Resource bundle tags read by this class 1.623 +static const char gMonthNames[] = "monthNames"; 1.624 + 1.625 +// Data flow in Calendar 1.626 +// --------------------- 1.627 + 1.628 +// The current time is represented in two ways by Calendar: as UTC 1.629 +// milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local 1.630 +// fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the 1.631 +// millis from the fields, and vice versa. The data needed to do this 1.632 +// conversion is encapsulated by a TimeZone object owned by the Calendar. 1.633 +// The data provided by the TimeZone object may also be overridden if the 1.634 +// user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class 1.635 +// keeps track of what information was most recently set by the caller, and 1.636 +// uses that to compute any other information as needed. 1.637 + 1.638 +// If the user sets the fields using set(), the data flow is as follows. 1.639 +// This is implemented by the Calendar subclass's computeTime() method. 1.640 +// During this process, certain fields may be ignored. The disambiguation 1.641 +// algorithm for resolving which fields to pay attention to is described 1.642 +// above. 1.643 + 1.644 +// local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.) 1.645 +// | 1.646 +// | Using Calendar-specific algorithm 1.647 +// V 1.648 +// local standard millis 1.649 +// | 1.650 +// | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET 1.651 +// V 1.652 +// UTC millis (in time data member) 1.653 + 1.654 +// If the user sets the UTC millis using setTime(), the data flow is as 1.655 +// follows. This is implemented by the Calendar subclass's computeFields() 1.656 +// method. 1.657 + 1.658 +// UTC millis (in time data member) 1.659 +// | 1.660 +// | Using TimeZone getOffset() 1.661 +// V 1.662 +// local standard millis 1.663 +// | 1.664 +// | Using Calendar-specific algorithm 1.665 +// V 1.666 +// local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.) 1.667 + 1.668 +// In general, a round trip from fields, through local and UTC millis, and 1.669 +// back out to fields is made when necessary. This is implemented by the 1.670 +// complete() method. Resolving a partial set of fields into a UTC millis 1.671 +// value allows all remaining fields to be generated from that value. If 1.672 +// the Calendar is lenient, the fields are also renormalized to standard 1.673 +// ranges when they are regenerated. 1.674 + 1.675 +// ------------------------------------- 1.676 + 1.677 +Calendar::Calendar(UErrorCode& success) 1.678 +: UObject(), 1.679 +fIsTimeSet(FALSE), 1.680 +fAreFieldsSet(FALSE), 1.681 +fAreAllFieldsSet(FALSE), 1.682 +fAreFieldsVirtuallySet(FALSE), 1.683 +fNextStamp((int32_t)kMinimumUserStamp), 1.684 +fTime(0), 1.685 +fLenient(TRUE), 1.686 +fZone(0), 1.687 +fRepeatedWallTime(UCAL_WALLTIME_LAST), 1.688 +fSkippedWallTime(UCAL_WALLTIME_LAST) 1.689 +{ 1.690 + clear(); 1.691 + fZone = TimeZone::createDefault(); 1.692 + if (fZone == NULL) { 1.693 + success = U_MEMORY_ALLOCATION_ERROR; 1.694 + } 1.695 + setWeekData(Locale::getDefault(), NULL, success); 1.696 +} 1.697 + 1.698 +// ------------------------------------- 1.699 + 1.700 +Calendar::Calendar(TimeZone* zone, const Locale& aLocale, UErrorCode& success) 1.701 +: UObject(), 1.702 +fIsTimeSet(FALSE), 1.703 +fAreFieldsSet(FALSE), 1.704 +fAreAllFieldsSet(FALSE), 1.705 +fAreFieldsVirtuallySet(FALSE), 1.706 +fNextStamp((int32_t)kMinimumUserStamp), 1.707 +fTime(0), 1.708 +fLenient(TRUE), 1.709 +fZone(0), 1.710 +fRepeatedWallTime(UCAL_WALLTIME_LAST), 1.711 +fSkippedWallTime(UCAL_WALLTIME_LAST) 1.712 +{ 1.713 + if(zone == 0) { 1.714 +#if defined (U_DEBUG_CAL) 1.715 + fprintf(stderr, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n", 1.716 + __FILE__, __LINE__); 1.717 +#endif 1.718 + success = U_ILLEGAL_ARGUMENT_ERROR; 1.719 + return; 1.720 + } 1.721 + 1.722 + clear(); 1.723 + fZone = zone; 1.724 + 1.725 + setWeekData(aLocale, NULL, success); 1.726 +} 1.727 + 1.728 +// ------------------------------------- 1.729 + 1.730 +Calendar::Calendar(const TimeZone& zone, const Locale& aLocale, UErrorCode& success) 1.731 +: UObject(), 1.732 +fIsTimeSet(FALSE), 1.733 +fAreFieldsSet(FALSE), 1.734 +fAreAllFieldsSet(FALSE), 1.735 +fAreFieldsVirtuallySet(FALSE), 1.736 +fNextStamp((int32_t)kMinimumUserStamp), 1.737 +fTime(0), 1.738 +fLenient(TRUE), 1.739 +fZone(0), 1.740 +fRepeatedWallTime(UCAL_WALLTIME_LAST), 1.741 +fSkippedWallTime(UCAL_WALLTIME_LAST) 1.742 +{ 1.743 + clear(); 1.744 + fZone = zone.clone(); 1.745 + if (fZone == NULL) { 1.746 + success = U_MEMORY_ALLOCATION_ERROR; 1.747 + } 1.748 + setWeekData(aLocale, NULL, success); 1.749 +} 1.750 + 1.751 +// ------------------------------------- 1.752 + 1.753 +Calendar::~Calendar() 1.754 +{ 1.755 + delete fZone; 1.756 +} 1.757 + 1.758 +// ------------------------------------- 1.759 + 1.760 +Calendar::Calendar(const Calendar &source) 1.761 +: UObject(source) 1.762 +{ 1.763 + fZone = 0; 1.764 + *this = source; 1.765 +} 1.766 + 1.767 +// ------------------------------------- 1.768 + 1.769 +Calendar & 1.770 +Calendar::operator=(const Calendar &right) 1.771 +{ 1.772 + if (this != &right) { 1.773 + uprv_arrayCopy(right.fFields, fFields, UCAL_FIELD_COUNT); 1.774 + uprv_arrayCopy(right.fIsSet, fIsSet, UCAL_FIELD_COUNT); 1.775 + uprv_arrayCopy(right.fStamp, fStamp, UCAL_FIELD_COUNT); 1.776 + fTime = right.fTime; 1.777 + fIsTimeSet = right.fIsTimeSet; 1.778 + fAreAllFieldsSet = right.fAreAllFieldsSet; 1.779 + fAreFieldsSet = right.fAreFieldsSet; 1.780 + fAreFieldsVirtuallySet = right.fAreFieldsVirtuallySet; 1.781 + fLenient = right.fLenient; 1.782 + fRepeatedWallTime = right.fRepeatedWallTime; 1.783 + fSkippedWallTime = right.fSkippedWallTime; 1.784 + if (fZone != NULL) { 1.785 + delete fZone; 1.786 + } 1.787 + if (right.fZone != NULL) { 1.788 + fZone = right.fZone->clone(); 1.789 + } 1.790 + fFirstDayOfWeek = right.fFirstDayOfWeek; 1.791 + fMinimalDaysInFirstWeek = right.fMinimalDaysInFirstWeek; 1.792 + fWeekendOnset = right.fWeekendOnset; 1.793 + fWeekendOnsetMillis = right.fWeekendOnsetMillis; 1.794 + fWeekendCease = right.fWeekendCease; 1.795 + fWeekendCeaseMillis = right.fWeekendCeaseMillis; 1.796 + fNextStamp = right.fNextStamp; 1.797 + uprv_strcpy(validLocale, right.validLocale); 1.798 + uprv_strcpy(actualLocale, right.actualLocale); 1.799 + } 1.800 + 1.801 + return *this; 1.802 +} 1.803 + 1.804 +// ------------------------------------- 1.805 + 1.806 +Calendar* U_EXPORT2 1.807 +Calendar::createInstance(UErrorCode& success) 1.808 +{ 1.809 + return createInstance(TimeZone::createDefault(), Locale::getDefault(), success); 1.810 +} 1.811 + 1.812 +// ------------------------------------- 1.813 + 1.814 +Calendar* U_EXPORT2 1.815 +Calendar::createInstance(const TimeZone& zone, UErrorCode& success) 1.816 +{ 1.817 + return createInstance(zone, Locale::getDefault(), success); 1.818 +} 1.819 + 1.820 +// ------------------------------------- 1.821 + 1.822 +Calendar* U_EXPORT2 1.823 +Calendar::createInstance(const Locale& aLocale, UErrorCode& success) 1.824 +{ 1.825 + return createInstance(TimeZone::createDefault(), aLocale, success); 1.826 +} 1.827 + 1.828 +// ------------------------------------- Adopting 1.829 + 1.830 +// Note: this is the bottleneck that actually calls the service routines. 1.831 + 1.832 +Calendar* U_EXPORT2 1.833 +Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success) 1.834 +{ 1.835 + if (U_FAILURE(success)) { 1.836 + return NULL; 1.837 + } 1.838 + 1.839 + Locale actualLoc; 1.840 + UObject* u = NULL; 1.841 + 1.842 +#if !UCONFIG_NO_SERVICE 1.843 + if (isCalendarServiceUsed()) { 1.844 + u = getCalendarService(success)->get(aLocale, LocaleKey::KIND_ANY, &actualLoc, success); 1.845 + } 1.846 + else 1.847 +#endif 1.848 + { 1.849 + u = createStandardCalendar(getCalendarTypeForLocale(aLocale.getName()), aLocale, success); 1.850 + } 1.851 + Calendar* c = NULL; 1.852 + 1.853 + if(U_FAILURE(success) || !u) { 1.854 + delete zone; 1.855 + if(U_SUCCESS(success)) { // Propagate some kind of err 1.856 + success = U_INTERNAL_PROGRAM_ERROR; 1.857 + } 1.858 + return NULL; 1.859 + } 1.860 + 1.861 +#if !UCONFIG_NO_SERVICE 1.862 + const UnicodeString* str = dynamic_cast<const UnicodeString*>(u); 1.863 + if(str != NULL) { 1.864 + // It's a unicode string telling us what type of calendar to load ("gregorian", etc) 1.865 + // Create a Locale over this string 1.866 + Locale l(""); 1.867 + LocaleUtility::initLocaleFromName(*str, l); 1.868 + 1.869 +#ifdef U_DEBUG_CALSVC 1.870 + fprintf(stderr, "Calendar::createInstance(%s), looking up [%s]\n", aLocale.getName(), l.getName()); 1.871 +#endif 1.872 + 1.873 + Locale actualLoc2; 1.874 + delete u; 1.875 + u = NULL; 1.876 + 1.877 + // Don't overwrite actualLoc, since the actual loc from this call 1.878 + // may be something like "@calendar=gregorian" -- TODO investigate 1.879 + // further... 1.880 + c = (Calendar*)getCalendarService(success)->get(l, LocaleKey::KIND_ANY, &actualLoc2, success); 1.881 + 1.882 + if(U_FAILURE(success) || !c) { 1.883 + delete zone; 1.884 + if(U_SUCCESS(success)) { 1.885 + success = U_INTERNAL_PROGRAM_ERROR; // Propagate some err 1.886 + } 1.887 + return NULL; 1.888 + } 1.889 + 1.890 + str = dynamic_cast<const UnicodeString*>(c); 1.891 + if(str != NULL) { 1.892 + // recursed! Second lookup returned a UnicodeString. 1.893 + // Perhaps DefaultCalendar{} was set to another locale. 1.894 +#ifdef U_DEBUG_CALSVC 1.895 + char tmp[200]; 1.896 + // Extract a char* out of it.. 1.897 + int32_t len = str->length(); 1.898 + int32_t actLen = sizeof(tmp)-1; 1.899 + if(len > actLen) { 1.900 + len = actLen; 1.901 + } 1.902 + str->extract(0,len,tmp); 1.903 + tmp[len]=0; 1.904 + 1.905 + fprintf(stderr, "err - recursed, 2nd lookup was unistring %s\n", tmp); 1.906 +#endif 1.907 + success = U_MISSING_RESOURCE_ERROR; // requested a calendar type which could NOT be found. 1.908 + delete c; 1.909 + delete zone; 1.910 + return NULL; 1.911 + } 1.912 +#ifdef U_DEBUG_CALSVC 1.913 + fprintf(stderr, "%p: setting week count data to locale %s, actual locale %s\n", c, (const char*)aLocale.getName(), (const char *)actualLoc.getName()); 1.914 +#endif 1.915 + c->setWeekData(aLocale, c->getType(), success); // set the correct locale (this was an indirected calendar) 1.916 + 1.917 + char keyword[ULOC_FULLNAME_CAPACITY]; 1.918 + UErrorCode tmpStatus = U_ZERO_ERROR; 1.919 + l.getKeywordValue("calendar", keyword, ULOC_FULLNAME_CAPACITY, tmpStatus); 1.920 + if (U_SUCCESS(tmpStatus) && uprv_strcmp(keyword, "iso8601") == 0) { 1.921 + c->setFirstDayOfWeek(UCAL_MONDAY); 1.922 + c->setMinimalDaysInFirstWeek(4); 1.923 + } 1.924 + } 1.925 + else 1.926 +#endif /* UCONFIG_NO_SERVICE */ 1.927 + { 1.928 + // a calendar was returned - we assume the factory did the right thing. 1.929 + c = (Calendar*)u; 1.930 + } 1.931 + 1.932 + // Now, reset calendar to default state: 1.933 + c->adoptTimeZone(zone); // Set the correct time zone 1.934 + c->setTimeInMillis(getNow(), success); // let the new calendar have the current time. 1.935 + 1.936 + return c; 1.937 +} 1.938 + 1.939 +// ------------------------------------- 1.940 + 1.941 +Calendar* U_EXPORT2 1.942 +Calendar::createInstance(const TimeZone& zone, const Locale& aLocale, UErrorCode& success) 1.943 +{ 1.944 + Calendar* c = createInstance(aLocale, success); 1.945 + if(U_SUCCESS(success) && c) { 1.946 + c->setTimeZone(zone); 1.947 + } 1.948 + return c; 1.949 +} 1.950 + 1.951 +// ------------------------------------- 1.952 + 1.953 +UBool 1.954 +Calendar::operator==(const Calendar& that) const 1.955 +{ 1.956 + UErrorCode status = U_ZERO_ERROR; 1.957 + return isEquivalentTo(that) && 1.958 + getTimeInMillis(status) == that.getTimeInMillis(status) && 1.959 + U_SUCCESS(status); 1.960 +} 1.961 + 1.962 +UBool 1.963 +Calendar::isEquivalentTo(const Calendar& other) const 1.964 +{ 1.965 + return typeid(*this) == typeid(other) && 1.966 + fLenient == other.fLenient && 1.967 + fRepeatedWallTime == other.fRepeatedWallTime && 1.968 + fSkippedWallTime == other.fSkippedWallTime && 1.969 + fFirstDayOfWeek == other.fFirstDayOfWeek && 1.970 + fMinimalDaysInFirstWeek == other.fMinimalDaysInFirstWeek && 1.971 + fWeekendOnset == other.fWeekendOnset && 1.972 + fWeekendOnsetMillis == other.fWeekendOnsetMillis && 1.973 + fWeekendCease == other.fWeekendCease && 1.974 + fWeekendCeaseMillis == other.fWeekendCeaseMillis && 1.975 + *fZone == *other.fZone; 1.976 +} 1.977 + 1.978 +// ------------------------------------- 1.979 + 1.980 +UBool 1.981 +Calendar::equals(const Calendar& when, UErrorCode& status) const 1.982 +{ 1.983 + return (this == &when || 1.984 + getTime(status) == when.getTime(status)); 1.985 +} 1.986 + 1.987 +// ------------------------------------- 1.988 + 1.989 +UBool 1.990 +Calendar::before(const Calendar& when, UErrorCode& status) const 1.991 +{ 1.992 + return (this != &when && 1.993 + getTimeInMillis(status) < when.getTimeInMillis(status)); 1.994 +} 1.995 + 1.996 +// ------------------------------------- 1.997 + 1.998 +UBool 1.999 +Calendar::after(const Calendar& when, UErrorCode& status) const 1.1000 +{ 1.1001 + return (this != &when && 1.1002 + getTimeInMillis(status) > when.getTimeInMillis(status)); 1.1003 +} 1.1004 + 1.1005 +// ------------------------------------- 1.1006 + 1.1007 + 1.1008 +const Locale* U_EXPORT2 1.1009 +Calendar::getAvailableLocales(int32_t& count) 1.1010 +{ 1.1011 + return Locale::getAvailableLocales(count); 1.1012 +} 1.1013 + 1.1014 +// ------------------------------------- 1.1015 + 1.1016 +StringEnumeration* U_EXPORT2 1.1017 +Calendar::getKeywordValuesForLocale(const char* key, 1.1018 + const Locale& locale, UBool commonlyUsed, UErrorCode& status) 1.1019 +{ 1.1020 + // This is a wrapper over ucal_getKeywordValuesForLocale 1.1021 + UEnumeration *uenum = ucal_getKeywordValuesForLocale(key, locale.getName(), 1.1022 + commonlyUsed, &status); 1.1023 + if (U_FAILURE(status)) { 1.1024 + uenum_close(uenum); 1.1025 + return NULL; 1.1026 + } 1.1027 + return new UStringEnumeration(uenum); 1.1028 +} 1.1029 + 1.1030 +// ------------------------------------- 1.1031 + 1.1032 +UDate U_EXPORT2 1.1033 +Calendar::getNow() 1.1034 +{ 1.1035 + return uprv_getUTCtime(); // return as milliseconds 1.1036 +} 1.1037 + 1.1038 +// ------------------------------------- 1.1039 + 1.1040 +/** 1.1041 +* Gets this Calendar's current time as a long. 1.1042 +* @return the current time as UTC milliseconds from the epoch. 1.1043 +*/ 1.1044 +double 1.1045 +Calendar::getTimeInMillis(UErrorCode& status) const 1.1046 +{ 1.1047 + if(U_FAILURE(status)) 1.1048 + return 0.0; 1.1049 + 1.1050 + if ( ! fIsTimeSet) 1.1051 + ((Calendar*)this)->updateTime(status); 1.1052 + 1.1053 + /* Test for buffer overflows */ 1.1054 + if(U_FAILURE(status)) { 1.1055 + return 0.0; 1.1056 + } 1.1057 + return fTime; 1.1058 +} 1.1059 + 1.1060 +// ------------------------------------- 1.1061 + 1.1062 +/** 1.1063 +* Sets this Calendar's current time from the given long value. 1.1064 +* A status of U_ILLEGAL_ARGUMENT_ERROR is set when millis is 1.1065 +* outside the range permitted by a Calendar object when not in lenient mode. 1.1066 +* when in lenient mode the out of range values are pinned to their respective min/max. 1.1067 +* @param date the new time in UTC milliseconds from the epoch. 1.1068 +*/ 1.1069 +void 1.1070 +Calendar::setTimeInMillis( double millis, UErrorCode& status ) { 1.1071 + if(U_FAILURE(status)) 1.1072 + return; 1.1073 + 1.1074 + if (millis > MAX_MILLIS) { 1.1075 + if(isLenient()) { 1.1076 + millis = MAX_MILLIS; 1.1077 + } else { 1.1078 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.1079 + return; 1.1080 + } 1.1081 + } else if (millis < MIN_MILLIS) { 1.1082 + if(isLenient()) { 1.1083 + millis = MIN_MILLIS; 1.1084 + } else { 1.1085 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.1086 + return; 1.1087 + } 1.1088 + } 1.1089 + 1.1090 + fTime = millis; 1.1091 + fAreFieldsSet = fAreAllFieldsSet = FALSE; 1.1092 + fIsTimeSet = fAreFieldsVirtuallySet = TRUE; 1.1093 + 1.1094 + for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) { 1.1095 + fFields[i] = 0; 1.1096 + fStamp[i] = kUnset; 1.1097 + fIsSet[i] = FALSE; 1.1098 + } 1.1099 + 1.1100 + 1.1101 +} 1.1102 + 1.1103 +// ------------------------------------- 1.1104 + 1.1105 +int32_t 1.1106 +Calendar::get(UCalendarDateFields field, UErrorCode& status) const 1.1107 +{ 1.1108 + // field values are only computed when actually requested; for more on when computation 1.1109 + // of various things happens, see the "data flow in Calendar" description at the top 1.1110 + // of this file 1.1111 + if (U_SUCCESS(status)) ((Calendar*)this)->complete(status); // Cast away const 1.1112 + return U_SUCCESS(status) ? fFields[field] : 0; 1.1113 +} 1.1114 + 1.1115 +// ------------------------------------- 1.1116 + 1.1117 +void 1.1118 +Calendar::set(UCalendarDateFields field, int32_t value) 1.1119 +{ 1.1120 + if (fAreFieldsVirtuallySet) { 1.1121 + UErrorCode ec = U_ZERO_ERROR; 1.1122 + computeFields(ec); 1.1123 + } 1.1124 + fFields[field] = value; 1.1125 + /* Ensure that the fNextStamp value doesn't go pass max value for int32_t */ 1.1126 + if (fNextStamp == STAMP_MAX) { 1.1127 + recalculateStamp(); 1.1128 + } 1.1129 + fStamp[field] = fNextStamp++; 1.1130 + fIsSet[field] = TRUE; // Remove later 1.1131 + fIsTimeSet = fAreFieldsSet = fAreFieldsVirtuallySet = FALSE; 1.1132 +} 1.1133 + 1.1134 +// ------------------------------------- 1.1135 + 1.1136 +void 1.1137 +Calendar::set(int32_t year, int32_t month, int32_t date) 1.1138 +{ 1.1139 + set(UCAL_YEAR, year); 1.1140 + set(UCAL_MONTH, month); 1.1141 + set(UCAL_DATE, date); 1.1142 +} 1.1143 + 1.1144 +// ------------------------------------- 1.1145 + 1.1146 +void 1.1147 +Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute) 1.1148 +{ 1.1149 + set(UCAL_YEAR, year); 1.1150 + set(UCAL_MONTH, month); 1.1151 + set(UCAL_DATE, date); 1.1152 + set(UCAL_HOUR_OF_DAY, hour); 1.1153 + set(UCAL_MINUTE, minute); 1.1154 +} 1.1155 + 1.1156 +// ------------------------------------- 1.1157 + 1.1158 +void 1.1159 +Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute, int32_t second) 1.1160 +{ 1.1161 + set(UCAL_YEAR, year); 1.1162 + set(UCAL_MONTH, month); 1.1163 + set(UCAL_DATE, date); 1.1164 + set(UCAL_HOUR_OF_DAY, hour); 1.1165 + set(UCAL_MINUTE, minute); 1.1166 + set(UCAL_SECOND, second); 1.1167 +} 1.1168 + 1.1169 +// ------------------------------------- 1.1170 + 1.1171 +void 1.1172 +Calendar::clear() 1.1173 +{ 1.1174 + for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) { 1.1175 + fFields[i] = 0; // Must do this; other code depends on it 1.1176 + fStamp[i] = kUnset; 1.1177 + fIsSet[i] = FALSE; // Remove later 1.1178 + } 1.1179 + fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE; 1.1180 + // fTime is not 'cleared' - may be used if no fields are set. 1.1181 +} 1.1182 + 1.1183 +// ------------------------------------- 1.1184 + 1.1185 +void 1.1186 +Calendar::clear(UCalendarDateFields field) 1.1187 +{ 1.1188 + if (fAreFieldsVirtuallySet) { 1.1189 + UErrorCode ec = U_ZERO_ERROR; 1.1190 + computeFields(ec); 1.1191 + } 1.1192 + fFields[field] = 0; 1.1193 + fStamp[field] = kUnset; 1.1194 + fIsSet[field] = FALSE; // Remove later 1.1195 + fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE; 1.1196 +} 1.1197 + 1.1198 +// ------------------------------------- 1.1199 + 1.1200 +UBool 1.1201 +Calendar::isSet(UCalendarDateFields field) const 1.1202 +{ 1.1203 + return fAreFieldsVirtuallySet || (fStamp[field] != kUnset); 1.1204 +} 1.1205 + 1.1206 + 1.1207 +int32_t Calendar::newestStamp(UCalendarDateFields first, UCalendarDateFields last, int32_t bestStampSoFar) const 1.1208 +{ 1.1209 + int32_t bestStamp = bestStampSoFar; 1.1210 + for (int32_t i=(int32_t)first; i<=(int32_t)last; ++i) { 1.1211 + if (fStamp[i] > bestStamp) { 1.1212 + bestStamp = fStamp[i]; 1.1213 + } 1.1214 + } 1.1215 + return bestStamp; 1.1216 +} 1.1217 + 1.1218 + 1.1219 +// ------------------------------------- 1.1220 + 1.1221 +void 1.1222 +Calendar::complete(UErrorCode& status) 1.1223 +{ 1.1224 + if (!fIsTimeSet) { 1.1225 + updateTime(status); 1.1226 + /* Test for buffer overflows */ 1.1227 + if(U_FAILURE(status)) { 1.1228 + return; 1.1229 + } 1.1230 + } 1.1231 + if (!fAreFieldsSet) { 1.1232 + computeFields(status); // fills in unset fields 1.1233 + /* Test for buffer overflows */ 1.1234 + if(U_FAILURE(status)) { 1.1235 + return; 1.1236 + } 1.1237 + fAreFieldsSet = TRUE; 1.1238 + fAreAllFieldsSet = TRUE; 1.1239 + } 1.1240 +} 1.1241 + 1.1242 +//------------------------------------------------------------------------- 1.1243 +// Protected utility methods for use by subclasses. These are very handy 1.1244 +// for implementing add, roll, and computeFields. 1.1245 +//------------------------------------------------------------------------- 1.1246 + 1.1247 +/** 1.1248 +* Adjust the specified field so that it is within 1.1249 +* the allowable range for the date to which this calendar is set. 1.1250 +* For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH} 1.1251 +* field for a calendar set to April 31 would cause it to be set 1.1252 +* to April 30. 1.1253 +* <p> 1.1254 +* <b>Subclassing:</b> 1.1255 +* <br> 1.1256 +* This utility method is intended for use by subclasses that need to implement 1.1257 +* their own overrides of {@link #roll roll} and {@link #add add}. 1.1258 +* <p> 1.1259 +* <b>Note:</b> 1.1260 +* <code>pinField</code> is implemented in terms of 1.1261 +* {@link #getActualMinimum getActualMinimum} 1.1262 +* and {@link #getActualMaximum getActualMaximum}. If either of those methods uses 1.1263 +* a slow, iterative algorithm for a particular field, it would be 1.1264 +* unwise to attempt to call <code>pinField</code> for that field. If you 1.1265 +* really do need to do so, you should override this method to do 1.1266 +* something more efficient for that field. 1.1267 +* <p> 1.1268 +* @param field The calendar field whose value should be pinned. 1.1269 +* 1.1270 +* @see #getActualMinimum 1.1271 +* @see #getActualMaximum 1.1272 +* @stable ICU 2.0 1.1273 +*/ 1.1274 +void Calendar::pinField(UCalendarDateFields field, UErrorCode& status) { 1.1275 + int32_t max = getActualMaximum(field, status); 1.1276 + int32_t min = getActualMinimum(field, status); 1.1277 + 1.1278 + if (fFields[field] > max) { 1.1279 + set(field, max); 1.1280 + } else if (fFields[field] < min) { 1.1281 + set(field, min); 1.1282 + } 1.1283 +} 1.1284 + 1.1285 + 1.1286 +void Calendar::computeFields(UErrorCode &ec) 1.1287 +{ 1.1288 + if (U_FAILURE(ec)) { 1.1289 + return; 1.1290 + } 1.1291 + // Compute local wall millis 1.1292 + double localMillis = internalGetTime(); 1.1293 + int32_t rawOffset, dstOffset; 1.1294 + getTimeZone().getOffset(localMillis, FALSE, rawOffset, dstOffset, ec); 1.1295 + localMillis += (rawOffset + dstOffset); 1.1296 + 1.1297 + // Mark fields as set. Do this before calling handleComputeFields(). 1.1298 + uint32_t mask = //fInternalSetMask; 1.1299 + (1 << UCAL_ERA) | 1.1300 + (1 << UCAL_YEAR) | 1.1301 + (1 << UCAL_MONTH) | 1.1302 + (1 << UCAL_DAY_OF_MONTH) | // = UCAL_DATE 1.1303 + (1 << UCAL_DAY_OF_YEAR) | 1.1304 + (1 << UCAL_EXTENDED_YEAR); 1.1305 + 1.1306 + for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) { 1.1307 + if ((mask & 1) == 0) { 1.1308 + fStamp[i] = kInternallySet; 1.1309 + fIsSet[i] = TRUE; // Remove later 1.1310 + } else { 1.1311 + fStamp[i] = kUnset; 1.1312 + fIsSet[i] = FALSE; // Remove later 1.1313 + } 1.1314 + mask >>= 1; 1.1315 + } 1.1316 + 1.1317 + // We used to check for and correct extreme millis values (near 1.1318 + // Long.MIN_VALUE or Long.MAX_VALUE) here. Such values would cause 1.1319 + // overflows from positive to negative (or vice versa) and had to 1.1320 + // be manually tweaked. We no longer need to do this because we 1.1321 + // have limited the range of supported dates to those that have a 1.1322 + // Julian day that fits into an int. This allows us to implement a 1.1323 + // JULIAN_DAY field and also removes some inelegant code. - Liu 1.1324 + // 11/6/00 1.1325 + 1.1326 + int32_t days = (int32_t)ClockMath::floorDivide(localMillis, (double)kOneDay); 1.1327 + 1.1328 + internalSet(UCAL_JULIAN_DAY,days + kEpochStartAsJulianDay); 1.1329 + 1.1330 +#if defined (U_DEBUG_CAL) 1.1331 + //fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n", 1.1332 + //__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis); 1.1333 +#endif 1.1334 + 1.1335 + computeGregorianAndDOWFields(fFields[UCAL_JULIAN_DAY], ec); 1.1336 + 1.1337 + // Call framework method to have subclass compute its fields. 1.1338 + // These must include, at a minimum, MONTH, DAY_OF_MONTH, 1.1339 + // EXTENDED_YEAR, YEAR, DAY_OF_YEAR. This method will call internalSet(), 1.1340 + // which will update stamp[]. 1.1341 + handleComputeFields(fFields[UCAL_JULIAN_DAY], ec); 1.1342 + 1.1343 + // Compute week-related fields, based on the subclass-computed 1.1344 + // fields computed by handleComputeFields(). 1.1345 + computeWeekFields(ec); 1.1346 + 1.1347 + // Compute time-related fields. These are indepent of the date and 1.1348 + // of the subclass algorithm. They depend only on the local zone 1.1349 + // wall milliseconds in day. 1.1350 + int32_t millisInDay = (int32_t) (localMillis - (days * kOneDay)); 1.1351 + fFields[UCAL_MILLISECONDS_IN_DAY] = millisInDay; 1.1352 + fFields[UCAL_MILLISECOND] = millisInDay % 1000; 1.1353 + millisInDay /= 1000; 1.1354 + fFields[UCAL_SECOND] = millisInDay % 60; 1.1355 + millisInDay /= 60; 1.1356 + fFields[UCAL_MINUTE] = millisInDay % 60; 1.1357 + millisInDay /= 60; 1.1358 + fFields[UCAL_HOUR_OF_DAY] = millisInDay; 1.1359 + fFields[UCAL_AM_PM] = millisInDay / 12; // Assume AM == 0 1.1360 + fFields[UCAL_HOUR] = millisInDay % 12; 1.1361 + fFields[UCAL_ZONE_OFFSET] = rawOffset; 1.1362 + fFields[UCAL_DST_OFFSET] = dstOffset; 1.1363 +} 1.1364 + 1.1365 +uint8_t Calendar::julianDayToDayOfWeek(double julian) 1.1366 +{ 1.1367 + // If julian is negative, then julian%7 will be negative, so we adjust 1.1368 + // accordingly. We add 1 because Julian day 0 is Monday. 1.1369 + int8_t dayOfWeek = (int8_t) uprv_fmod(julian + 1, 7); 1.1370 + 1.1371 + uint8_t result = (uint8_t)(dayOfWeek + ((dayOfWeek < 0) ? (7+UCAL_SUNDAY ) : UCAL_SUNDAY)); 1.1372 + return result; 1.1373 +} 1.1374 + 1.1375 +/** 1.1376 +* Compute the Gregorian calendar year, month, and day of month from 1.1377 +* the given Julian day. These values are not stored in fields, but in 1.1378 +* member variables gregorianXxx. Also compute the DAY_OF_WEEK and 1.1379 +* DOW_LOCAL fields. 1.1380 +*/ 1.1381 +void Calendar::computeGregorianAndDOWFields(int32_t julianDay, UErrorCode &ec) 1.1382 +{ 1.1383 + computeGregorianFields(julianDay, ec); 1.1384 + 1.1385 + // Compute day of week: JD 0 = Monday 1.1386 + int32_t dow = julianDayToDayOfWeek(julianDay); 1.1387 + internalSet(UCAL_DAY_OF_WEEK,dow); 1.1388 + 1.1389 + // Calculate 1-based localized day of week 1.1390 + int32_t dowLocal = dow - getFirstDayOfWeek() + 1; 1.1391 + if (dowLocal < 1) { 1.1392 + dowLocal += 7; 1.1393 + } 1.1394 + internalSet(UCAL_DOW_LOCAL,dowLocal); 1.1395 + fFields[UCAL_DOW_LOCAL] = dowLocal; 1.1396 +} 1.1397 + 1.1398 +/** 1.1399 +* Compute the Gregorian calendar year, month, and day of month from the 1.1400 +* Julian day. These values are not stored in fields, but in member 1.1401 +* variables gregorianXxx. They are used for time zone computations and by 1.1402 +* subclasses that are Gregorian derivatives. Subclasses may call this 1.1403 +* method to perform a Gregorian calendar millis->fields computation. 1.1404 +*/ 1.1405 +void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode & /* ec */) { 1.1406 + int32_t gregorianDayOfWeekUnused; 1.1407 + Grego::dayToFields(julianDay - kEpochStartAsJulianDay, fGregorianYear, fGregorianMonth, fGregorianDayOfMonth, gregorianDayOfWeekUnused, fGregorianDayOfYear); 1.1408 +} 1.1409 + 1.1410 +/** 1.1411 +* Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH, 1.1412 +* DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR, 1.1413 +* DAY_OF_WEEK, and DAY_OF_YEAR. The latter fields are computed by the 1.1414 +* subclass based on the calendar system. 1.1415 +* 1.1416 +* <p>The YEAR_WOY field is computed simplistically. It is equal to YEAR 1.1417 +* most of the time, but at the year boundary it may be adjusted to YEAR-1 1.1418 +* or YEAR+1 to reflect the overlap of a week into an adjacent year. In 1.1419 +* this case, a simple increment or decrement is performed on YEAR, even 1.1420 +* though this may yield an invalid YEAR value. For instance, if the YEAR 1.1421 +* is part of a calendar system with an N-year cycle field CYCLE, then 1.1422 +* incrementing the YEAR may involve incrementing CYCLE and setting YEAR 1.1423 +* back to 0 or 1. This is not handled by this code, and in fact cannot be 1.1424 +* simply handled without having subclasses define an entire parallel set of 1.1425 +* fields for fields larger than or equal to a year. This additional 1.1426 +* complexity is not warranted, since the intention of the YEAR_WOY field is 1.1427 +* to support ISO 8601 notation, so it will typically be used with a 1.1428 +* proleptic Gregorian calendar, which has no field larger than a year. 1.1429 +*/ 1.1430 +void Calendar::computeWeekFields(UErrorCode &ec) { 1.1431 + if(U_FAILURE(ec)) { 1.1432 + return; 1.1433 + } 1.1434 + int32_t eyear = fFields[UCAL_EXTENDED_YEAR]; 1.1435 + int32_t dayOfWeek = fFields[UCAL_DAY_OF_WEEK]; 1.1436 + int32_t dayOfYear = fFields[UCAL_DAY_OF_YEAR]; 1.1437 + 1.1438 + // WEEK_OF_YEAR start 1.1439 + // Compute the week of the year. For the Gregorian calendar, valid week 1.1440 + // numbers run from 1 to 52 or 53, depending on the year, the first day 1.1441 + // of the week, and the minimal days in the first week. For other 1.1442 + // calendars, the valid range may be different -- it depends on the year 1.1443 + // length. Days at the start of the year may fall into the last week of 1.1444 + // the previous year; days at the end of the year may fall into the 1.1445 + // first week of the next year. ASSUME that the year length is less than 1.1446 + // 7000 days. 1.1447 + int32_t yearOfWeekOfYear = eyear; 1.1448 + int32_t relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; // 0..6 1.1449 + int32_t relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; // 0..6 1.1450 + int32_t woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53 1.1451 + if ((7 - relDowJan1) >= getMinimalDaysInFirstWeek()) { 1.1452 + ++woy; 1.1453 + } 1.1454 + 1.1455 + // Adjust for weeks at the year end that overlap into the previous or 1.1456 + // next calendar year. 1.1457 + if (woy == 0) { 1.1458 + // We are the last week of the previous year. 1.1459 + // Check to see if we are in the last week; if so, we need 1.1460 + // to handle the case in which we are the first week of the 1.1461 + // next year. 1.1462 + 1.1463 + int32_t prevDoy = dayOfYear + handleGetYearLength(eyear - 1); 1.1464 + woy = weekNumber(prevDoy, dayOfWeek); 1.1465 + yearOfWeekOfYear--; 1.1466 + } else { 1.1467 + int32_t lastDoy = handleGetYearLength(eyear); 1.1468 + // Fast check: For it to be week 1 of the next year, the DOY 1.1469 + // must be on or after L-5, where L is yearLength(), then it 1.1470 + // cannot possibly be week 1 of the next year: 1.1471 + // L-5 L 1.1472 + // doy: 359 360 361 362 363 364 365 001 1.1473 + // dow: 1 2 3 4 5 6 7 1.1474 + if (dayOfYear >= (lastDoy - 5)) { 1.1475 + int32_t lastRelDow = (relDow + lastDoy - dayOfYear) % 7; 1.1476 + if (lastRelDow < 0) { 1.1477 + lastRelDow += 7; 1.1478 + } 1.1479 + if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) && 1.1480 + ((dayOfYear + 7 - relDow) > lastDoy)) { 1.1481 + woy = 1; 1.1482 + yearOfWeekOfYear++; 1.1483 + } 1.1484 + } 1.1485 + } 1.1486 + fFields[UCAL_WEEK_OF_YEAR] = woy; 1.1487 + fFields[UCAL_YEAR_WOY] = yearOfWeekOfYear; 1.1488 + // WEEK_OF_YEAR end 1.1489 + 1.1490 + int32_t dayOfMonth = fFields[UCAL_DAY_OF_MONTH]; 1.1491 + fFields[UCAL_WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek); 1.1492 + fFields[UCAL_DAY_OF_WEEK_IN_MONTH] = (dayOfMonth-1) / 7 + 1; 1.1493 +#if defined (U_DEBUG_CAL) 1.1494 + if(fFields[UCAL_DAY_OF_WEEK_IN_MONTH]==0) fprintf(stderr, "%s:%d: DOWIM %d on %g\n", 1.1495 + __FILE__, __LINE__,fFields[UCAL_DAY_OF_WEEK_IN_MONTH], fTime); 1.1496 +#endif 1.1497 +} 1.1498 + 1.1499 + 1.1500 +int32_t Calendar::weekNumber(int32_t desiredDay, int32_t dayOfPeriod, int32_t dayOfWeek) 1.1501 +{ 1.1502 + // Determine the day of the week of the first day of the period 1.1503 + // in question (either a year or a month). Zero represents the 1.1504 + // first day of the week on this calendar. 1.1505 + int32_t periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % 7; 1.1506 + if (periodStartDayOfWeek < 0) periodStartDayOfWeek += 7; 1.1507 + 1.1508 + // Compute the week number. Initially, ignore the first week, which 1.1509 + // may be fractional (or may not be). We add periodStartDayOfWeek in 1.1510 + // order to fill out the first week, if it is fractional. 1.1511 + int32_t weekNo = (desiredDay + periodStartDayOfWeek - 1)/7; 1.1512 + 1.1513 + // If the first week is long enough, then count it. If 1.1514 + // the minimal days in the first week is one, or if the period start 1.1515 + // is zero, we always increment weekNo. 1.1516 + if ((7 - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo; 1.1517 + 1.1518 + return weekNo; 1.1519 +} 1.1520 + 1.1521 +void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode &/* status */) 1.1522 +{ 1.1523 + internalSet(UCAL_MONTH, getGregorianMonth()); 1.1524 + internalSet(UCAL_DAY_OF_MONTH, getGregorianDayOfMonth()); 1.1525 + internalSet(UCAL_DAY_OF_YEAR, getGregorianDayOfYear()); 1.1526 + int32_t eyear = getGregorianYear(); 1.1527 + internalSet(UCAL_EXTENDED_YEAR, eyear); 1.1528 + int32_t era = GregorianCalendar::AD; 1.1529 + if (eyear < 1) { 1.1530 + era = GregorianCalendar::BC; 1.1531 + eyear = 1 - eyear; 1.1532 + } 1.1533 + internalSet(UCAL_ERA, era); 1.1534 + internalSet(UCAL_YEAR, eyear); 1.1535 +} 1.1536 +// ------------------------------------- 1.1537 + 1.1538 + 1.1539 +void Calendar::roll(EDateFields field, int32_t amount, UErrorCode& status) 1.1540 +{ 1.1541 + roll((UCalendarDateFields)field, amount, status); 1.1542 +} 1.1543 + 1.1544 +void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) 1.1545 +{ 1.1546 + if (amount == 0) { 1.1547 + return; // Nothing to do 1.1548 + } 1.1549 + 1.1550 + complete(status); 1.1551 + 1.1552 + if(U_FAILURE(status)) { 1.1553 + return; 1.1554 + } 1.1555 + switch (field) { 1.1556 + case UCAL_DAY_OF_MONTH: 1.1557 + case UCAL_AM_PM: 1.1558 + case UCAL_MINUTE: 1.1559 + case UCAL_SECOND: 1.1560 + case UCAL_MILLISECOND: 1.1561 + case UCAL_MILLISECONDS_IN_DAY: 1.1562 + case UCAL_ERA: 1.1563 + // These are the standard roll instructions. These work for all 1.1564 + // simple cases, that is, cases in which the limits are fixed, such 1.1565 + // as the hour, the day of the month, and the era. 1.1566 + { 1.1567 + int32_t min = getActualMinimum(field,status); 1.1568 + int32_t max = getActualMaximum(field,status); 1.1569 + int32_t gap = max - min + 1; 1.1570 + 1.1571 + int32_t value = internalGet(field) + amount; 1.1572 + value = (value - min) % gap; 1.1573 + if (value < 0) { 1.1574 + value += gap; 1.1575 + } 1.1576 + value += min; 1.1577 + 1.1578 + set(field, value); 1.1579 + return; 1.1580 + } 1.1581 + 1.1582 + case UCAL_HOUR: 1.1583 + case UCAL_HOUR_OF_DAY: 1.1584 + // Rolling the hour is difficult on the ONSET and CEASE days of 1.1585 + // daylight savings. For example, if the change occurs at 1.1586 + // 2 AM, we have the following progression: 1.1587 + // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst 1.1588 + // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std 1.1589 + // To get around this problem we don't use fields; we manipulate 1.1590 + // the time in millis directly. 1.1591 + { 1.1592 + // Assume min == 0 in calculations below 1.1593 + double start = getTimeInMillis(status); 1.1594 + int32_t oldHour = internalGet(field); 1.1595 + int32_t max = getMaximum(field); 1.1596 + int32_t newHour = (oldHour + amount) % (max + 1); 1.1597 + if (newHour < 0) { 1.1598 + newHour += max + 1; 1.1599 + } 1.1600 + setTimeInMillis(start + kOneHour * (newHour - oldHour),status); 1.1601 + return; 1.1602 + } 1.1603 + 1.1604 + case UCAL_MONTH: 1.1605 + // Rolling the month involves both pinning the final value 1.1606 + // and adjusting the DAY_OF_MONTH if necessary. We only adjust the 1.1607 + // DAY_OF_MONTH if, after updating the MONTH field, it is illegal. 1.1608 + // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>. 1.1609 + { 1.1610 + int32_t max = getActualMaximum(UCAL_MONTH, status); 1.1611 + int32_t mon = (internalGet(UCAL_MONTH) + amount) % (max+1); 1.1612 + 1.1613 + if (mon < 0) { 1.1614 + mon += (max + 1); 1.1615 + } 1.1616 + set(UCAL_MONTH, mon); 1.1617 + 1.1618 + // Keep the day of month in range. We don't want to spill over 1.1619 + // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 -> 1.1620 + // mar3. 1.1621 + pinField(UCAL_DAY_OF_MONTH,status); 1.1622 + return; 1.1623 + } 1.1624 + 1.1625 + case UCAL_YEAR: 1.1626 + case UCAL_YEAR_WOY: 1.1627 + { 1.1628 + // * If era==0 and years go backwards in time, change sign of amount. 1.1629 + // * Until we have new API per #9393, we temporarily hardcode knowledge of 1.1630 + // which calendars have era 0 years that go backwards. 1.1631 + UBool era0WithYearsThatGoBackwards = FALSE; 1.1632 + int32_t era = get(UCAL_ERA, status); 1.1633 + if (era == 0) { 1.1634 + const char * calType = getType(); 1.1635 + if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) { 1.1636 + amount = -amount; 1.1637 + era0WithYearsThatGoBackwards = TRUE; 1.1638 + } 1.1639 + } 1.1640 + int32_t newYear = internalGet(field) + amount; 1.1641 + if (era > 0 || newYear >= 1) { 1.1642 + int32_t maxYear = getActualMaximum(field, status); 1.1643 + if (maxYear < 32768) { 1.1644 + // this era has real bounds, roll should wrap years 1.1645 + if (newYear < 1) { 1.1646 + newYear = maxYear - ((-newYear) % maxYear); 1.1647 + } else if (newYear > maxYear) { 1.1648 + newYear = ((newYear - 1) % maxYear) + 1; 1.1649 + } 1.1650 + // else era is unbounded, just pin low year instead of wrapping 1.1651 + } else if (newYear < 1) { 1.1652 + newYear = 1; 1.1653 + } 1.1654 + // else we are in era 0 with newYear < 1; 1.1655 + // calendars with years that go backwards must pin the year value at 0, 1.1656 + // other calendars can have years < 0 in era 0 1.1657 + } else if (era0WithYearsThatGoBackwards) { 1.1658 + newYear = 1; 1.1659 + } 1.1660 + set(field, newYear); 1.1661 + pinField(UCAL_MONTH,status); 1.1662 + pinField(UCAL_DAY_OF_MONTH,status); 1.1663 + return; 1.1664 + } 1.1665 + 1.1666 + case UCAL_EXTENDED_YEAR: 1.1667 + // Rolling the year can involve pinning the DAY_OF_MONTH. 1.1668 + set(field, internalGet(field) + amount); 1.1669 + pinField(UCAL_MONTH,status); 1.1670 + pinField(UCAL_DAY_OF_MONTH,status); 1.1671 + return; 1.1672 + 1.1673 + case UCAL_WEEK_OF_MONTH: 1.1674 + { 1.1675 + // This is tricky, because during the roll we may have to shift 1.1676 + // to a different day of the week. For example: 1.1677 + 1.1678 + // s m t w r f s 1.1679 + // 1 2 3 4 5 1.1680 + // 6 7 8 9 10 11 12 1.1681 + 1.1682 + // When rolling from the 6th or 7th back one week, we go to the 1.1683 + // 1st (assuming that the first partial week counts). The same 1.1684 + // thing happens at the end of the month. 1.1685 + 1.1686 + // The other tricky thing is that we have to figure out whether 1.1687 + // the first partial week actually counts or not, based on the 1.1688 + // minimal first days in the week. And we have to use the 1.1689 + // correct first day of the week to delineate the week 1.1690 + // boundaries. 1.1691 + 1.1692 + // Here's our algorithm. First, we find the real boundaries of 1.1693 + // the month. Then we discard the first partial week if it 1.1694 + // doesn't count in this locale. Then we fill in the ends with 1.1695 + // phantom days, so that the first partial week and the last 1.1696 + // partial week are full weeks. We then have a nice square 1.1697 + // block of weeks. We do the usual rolling within this block, 1.1698 + // as is done elsewhere in this method. If we wind up on one of 1.1699 + // the phantom days that we added, we recognize this and pin to 1.1700 + // the first or the last day of the month. Easy, eh? 1.1701 + 1.1702 + // Normalize the DAY_OF_WEEK so that 0 is the first day of the week 1.1703 + // in this locale. We have dow in 0..6. 1.1704 + int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); 1.1705 + if (dow < 0) dow += 7; 1.1706 + 1.1707 + // Find the day of the week (normalized for locale) for the first 1.1708 + // of the month. 1.1709 + int32_t fdm = (dow - internalGet(UCAL_DAY_OF_MONTH) + 1) % 7; 1.1710 + if (fdm < 0) fdm += 7; 1.1711 + 1.1712 + // Get the first day of the first full week of the month, 1.1713 + // including phantom days, if any. Figure out if the first week 1.1714 + // counts or not; if it counts, then fill in phantom days. If 1.1715 + // not, advance to the first real full week (skip the partial week). 1.1716 + int32_t start; 1.1717 + if ((7 - fdm) < getMinimalDaysInFirstWeek()) 1.1718 + start = 8 - fdm; // Skip the first partial week 1.1719 + else 1.1720 + start = 1 - fdm; // This may be zero or negative 1.1721 + 1.1722 + // Get the day of the week (normalized for locale) for the last 1.1723 + // day of the month. 1.1724 + int32_t monthLen = getActualMaximum(UCAL_DAY_OF_MONTH, status); 1.1725 + int32_t ldm = (monthLen - internalGet(UCAL_DAY_OF_MONTH) + dow) % 7; 1.1726 + // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here. 1.1727 + 1.1728 + // Get the limit day for the blocked-off rectangular month; that 1.1729 + // is, the day which is one past the last day of the month, 1.1730 + // after the month has already been filled in with phantom days 1.1731 + // to fill out the last week. This day has a normalized DOW of 0. 1.1732 + int32_t limit = monthLen + 7 - ldm; 1.1733 + 1.1734 + // Now roll between start and (limit - 1). 1.1735 + int32_t gap = limit - start; 1.1736 + int32_t day_of_month = (internalGet(UCAL_DAY_OF_MONTH) + amount*7 - 1.1737 + start) % gap; 1.1738 + if (day_of_month < 0) day_of_month += gap; 1.1739 + day_of_month += start; 1.1740 + 1.1741 + // Finally, pin to the real start and end of the month. 1.1742 + if (day_of_month < 1) day_of_month = 1; 1.1743 + if (day_of_month > monthLen) day_of_month = monthLen; 1.1744 + 1.1745 + // Set the DAY_OF_MONTH. We rely on the fact that this field 1.1746 + // takes precedence over everything else (since all other fields 1.1747 + // are also set at this point). If this fact changes (if the 1.1748 + // disambiguation algorithm changes) then we will have to unset 1.1749 + // the appropriate fields here so that DAY_OF_MONTH is attended 1.1750 + // to. 1.1751 + set(UCAL_DAY_OF_MONTH, day_of_month); 1.1752 + return; 1.1753 + } 1.1754 + case UCAL_WEEK_OF_YEAR: 1.1755 + { 1.1756 + // This follows the outline of WEEK_OF_MONTH, except it applies 1.1757 + // to the whole year. Please see the comment for WEEK_OF_MONTH 1.1758 + // for general notes. 1.1759 + 1.1760 + // Normalize the DAY_OF_WEEK so that 0 is the first day of the week 1.1761 + // in this locale. We have dow in 0..6. 1.1762 + int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); 1.1763 + if (dow < 0) dow += 7; 1.1764 + 1.1765 + // Find the day of the week (normalized for locale) for the first 1.1766 + // of the year. 1.1767 + int32_t fdy = (dow - internalGet(UCAL_DAY_OF_YEAR) + 1) % 7; 1.1768 + if (fdy < 0) fdy += 7; 1.1769 + 1.1770 + // Get the first day of the first full week of the year, 1.1771 + // including phantom days, if any. Figure out if the first week 1.1772 + // counts or not; if it counts, then fill in phantom days. If 1.1773 + // not, advance to the first real full week (skip the partial week). 1.1774 + int32_t start; 1.1775 + if ((7 - fdy) < getMinimalDaysInFirstWeek()) 1.1776 + start = 8 - fdy; // Skip the first partial week 1.1777 + else 1.1778 + start = 1 - fdy; // This may be zero or negative 1.1779 + 1.1780 + // Get the day of the week (normalized for locale) for the last 1.1781 + // day of the year. 1.1782 + int32_t yearLen = getActualMaximum(UCAL_DAY_OF_YEAR,status); 1.1783 + int32_t ldy = (yearLen - internalGet(UCAL_DAY_OF_YEAR) + dow) % 7; 1.1784 + // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here. 1.1785 + 1.1786 + // Get the limit day for the blocked-off rectangular year; that 1.1787 + // is, the day which is one past the last day of the year, 1.1788 + // after the year has already been filled in with phantom days 1.1789 + // to fill out the last week. This day has a normalized DOW of 0. 1.1790 + int32_t limit = yearLen + 7 - ldy; 1.1791 + 1.1792 + // Now roll between start and (limit - 1). 1.1793 + int32_t gap = limit - start; 1.1794 + int32_t day_of_year = (internalGet(UCAL_DAY_OF_YEAR) + amount*7 - 1.1795 + start) % gap; 1.1796 + if (day_of_year < 0) day_of_year += gap; 1.1797 + day_of_year += start; 1.1798 + 1.1799 + // Finally, pin to the real start and end of the month. 1.1800 + if (day_of_year < 1) day_of_year = 1; 1.1801 + if (day_of_year > yearLen) day_of_year = yearLen; 1.1802 + 1.1803 + // Make sure that the year and day of year are attended to by 1.1804 + // clearing other fields which would normally take precedence. 1.1805 + // If the disambiguation algorithm is changed, this section will 1.1806 + // have to be updated as well. 1.1807 + set(UCAL_DAY_OF_YEAR, day_of_year); 1.1808 + clear(UCAL_MONTH); 1.1809 + return; 1.1810 + } 1.1811 + case UCAL_DAY_OF_YEAR: 1.1812 + { 1.1813 + // Roll the day of year using millis. Compute the millis for 1.1814 + // the start of the year, and get the length of the year. 1.1815 + double delta = amount * kOneDay; // Scale up from days to millis 1.1816 + double min2 = internalGet(UCAL_DAY_OF_YEAR)-1; 1.1817 + min2 *= kOneDay; 1.1818 + min2 = internalGetTime() - min2; 1.1819 + 1.1820 + // double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay; 1.1821 + double newtime; 1.1822 + 1.1823 + double yearLength = getActualMaximum(UCAL_DAY_OF_YEAR,status); 1.1824 + double oneYear = yearLength; 1.1825 + oneYear *= kOneDay; 1.1826 + newtime = uprv_fmod((internalGetTime() + delta - min2), oneYear); 1.1827 + if (newtime < 0) newtime += oneYear; 1.1828 + setTimeInMillis(newtime + min2, status); 1.1829 + return; 1.1830 + } 1.1831 + case UCAL_DAY_OF_WEEK: 1.1832 + case UCAL_DOW_LOCAL: 1.1833 + { 1.1834 + // Roll the day of week using millis. Compute the millis for 1.1835 + // the start of the week, using the first day of week setting. 1.1836 + // Restrict the millis to [start, start+7days). 1.1837 + double delta = amount * kOneDay; // Scale up from days to millis 1.1838 + // Compute the number of days before the current day in this 1.1839 + // week. This will be a value 0..6. 1.1840 + int32_t leadDays = internalGet(field); 1.1841 + leadDays -= (field == UCAL_DAY_OF_WEEK) ? getFirstDayOfWeek() : 1; 1.1842 + if (leadDays < 0) leadDays += 7; 1.1843 + double min2 = internalGetTime() - leadDays * kOneDay; 1.1844 + double newtime = uprv_fmod((internalGetTime() + delta - min2), kOneWeek); 1.1845 + if (newtime < 0) newtime += kOneWeek; 1.1846 + setTimeInMillis(newtime + min2, status); 1.1847 + return; 1.1848 + } 1.1849 + case UCAL_DAY_OF_WEEK_IN_MONTH: 1.1850 + { 1.1851 + // Roll the day of week in the month using millis. Determine 1.1852 + // the first day of the week in the month, and then the last, 1.1853 + // and then roll within that range. 1.1854 + double delta = amount * kOneWeek; // Scale up from weeks to millis 1.1855 + // Find the number of same days of the week before this one 1.1856 + // in this month. 1.1857 + int32_t preWeeks = (internalGet(UCAL_DAY_OF_MONTH) - 1) / 7; 1.1858 + // Find the number of same days of the week after this one 1.1859 + // in this month. 1.1860 + int32_t postWeeks = (getActualMaximum(UCAL_DAY_OF_MONTH,status) - 1.1861 + internalGet(UCAL_DAY_OF_MONTH)) / 7; 1.1862 + // From these compute the min and gap millis for rolling. 1.1863 + double min2 = internalGetTime() - preWeeks * kOneWeek; 1.1864 + double gap2 = kOneWeek * (preWeeks + postWeeks + 1); // Must add 1! 1.1865 + // Roll within this range 1.1866 + double newtime = uprv_fmod((internalGetTime() + delta - min2), gap2); 1.1867 + if (newtime < 0) newtime += gap2; 1.1868 + setTimeInMillis(newtime + min2, status); 1.1869 + return; 1.1870 + } 1.1871 + case UCAL_JULIAN_DAY: 1.1872 + set(field, internalGet(field) + amount); 1.1873 + return; 1.1874 + default: 1.1875 + // Other fields cannot be rolled by this method 1.1876 +#if defined (U_DEBUG_CAL) 1.1877 + fprintf(stderr, "%s:%d: ILLEGAL ARG because of roll on non-rollable field %s\n", 1.1878 + __FILE__, __LINE__,fldName(field)); 1.1879 +#endif 1.1880 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.1881 + } 1.1882 +} 1.1883 + 1.1884 +void Calendar::add(EDateFields field, int32_t amount, UErrorCode& status) 1.1885 +{ 1.1886 + Calendar::add((UCalendarDateFields)field, amount, status); 1.1887 +} 1.1888 + 1.1889 +// ------------------------------------- 1.1890 +void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) 1.1891 +{ 1.1892 + if (amount == 0) { 1.1893 + return; // Do nothing! 1.1894 + } 1.1895 + 1.1896 + // We handle most fields in the same way. The algorithm is to add 1.1897 + // a computed amount of millis to the current millis. The only 1.1898 + // wrinkle is with DST (and/or a change to the zone's UTC offset, which 1.1899 + // we'll include with DST) -- for some fields, like the DAY_OF_MONTH, 1.1900 + // we don't want the HOUR to shift due to changes in DST. If the 1.1901 + // result of the add operation is to move from DST to Standard, or 1.1902 + // vice versa, we need to adjust by an hour forward or back, 1.1903 + // respectively. For such fields we set keepHourInvariant to TRUE. 1.1904 + 1.1905 + // We only adjust the DST for fields larger than an hour. For 1.1906 + // fields smaller than an hour, we cannot adjust for DST without 1.1907 + // causing problems. for instance, if you add one hour to April 5, 1.1908 + // 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an 1.1909 + // illegal value), but then the adjustment sees the change and 1.1910 + // compensates by subtracting an hour. As a result the time 1.1911 + // doesn't advance at all. 1.1912 + 1.1913 + // For some fields larger than a day, such as a UCAL_MONTH, we pin the 1.1914 + // UCAL_DAY_OF_MONTH. This allows <March 31>.add(UCAL_MONTH, 1) to be 1.1915 + // <April 30>, rather than <April 31> => <May 1>. 1.1916 + 1.1917 + double delta = amount; // delta in ms 1.1918 + UBool keepHourInvariant = TRUE; 1.1919 + 1.1920 + switch (field) { 1.1921 + case UCAL_ERA: 1.1922 + set(field, get(field, status) + amount); 1.1923 + pinField(UCAL_ERA, status); 1.1924 + return; 1.1925 + 1.1926 + case UCAL_YEAR: 1.1927 + case UCAL_YEAR_WOY: 1.1928 + { 1.1929 + // * If era=0 and years go backwards in time, change sign of amount. 1.1930 + // * Until we have new API per #9393, we temporarily hardcode knowledge of 1.1931 + // which calendars have era 0 years that go backwards. 1.1932 + // * Note that for UCAL_YEAR (but not UCAL_YEAR_WOY) we could instead handle 1.1933 + // this by applying the amount to the UCAL_EXTENDED_YEAR field; but since 1.1934 + // we would still need to handle UCAL_YEAR_WOY as below, might as well 1.1935 + // also handle UCAL_YEAR the same way. 1.1936 + int32_t era = get(UCAL_ERA, status); 1.1937 + if (era == 0) { 1.1938 + const char * calType = getType(); 1.1939 + if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) { 1.1940 + amount = -amount; 1.1941 + } 1.1942 + } 1.1943 + } 1.1944 + // Fall through into normal handling 1.1945 + case UCAL_EXTENDED_YEAR: 1.1946 + case UCAL_MONTH: 1.1947 + { 1.1948 + UBool oldLenient = isLenient(); 1.1949 + setLenient(TRUE); 1.1950 + set(field, get(field, status) + amount); 1.1951 + pinField(UCAL_DAY_OF_MONTH, status); 1.1952 + if(oldLenient==FALSE) { 1.1953 + complete(status); /* force recalculate */ 1.1954 + setLenient(oldLenient); 1.1955 + } 1.1956 + } 1.1957 + return; 1.1958 + 1.1959 + case UCAL_WEEK_OF_YEAR: 1.1960 + case UCAL_WEEK_OF_MONTH: 1.1961 + case UCAL_DAY_OF_WEEK_IN_MONTH: 1.1962 + delta *= kOneWeek; 1.1963 + break; 1.1964 + 1.1965 + case UCAL_AM_PM: 1.1966 + delta *= 12 * kOneHour; 1.1967 + break; 1.1968 + 1.1969 + case UCAL_DAY_OF_MONTH: 1.1970 + case UCAL_DAY_OF_YEAR: 1.1971 + case UCAL_DAY_OF_WEEK: 1.1972 + case UCAL_DOW_LOCAL: 1.1973 + case UCAL_JULIAN_DAY: 1.1974 + delta *= kOneDay; 1.1975 + break; 1.1976 + 1.1977 + case UCAL_HOUR_OF_DAY: 1.1978 + case UCAL_HOUR: 1.1979 + delta *= kOneHour; 1.1980 + keepHourInvariant = FALSE; 1.1981 + break; 1.1982 + 1.1983 + case UCAL_MINUTE: 1.1984 + delta *= kOneMinute; 1.1985 + keepHourInvariant = FALSE; 1.1986 + break; 1.1987 + 1.1988 + case UCAL_SECOND: 1.1989 + delta *= kOneSecond; 1.1990 + keepHourInvariant = FALSE; 1.1991 + break; 1.1992 + 1.1993 + case UCAL_MILLISECOND: 1.1994 + case UCAL_MILLISECONDS_IN_DAY: 1.1995 + keepHourInvariant = FALSE; 1.1996 + break; 1.1997 + 1.1998 + default: 1.1999 +#if defined (U_DEBUG_CAL) 1.2000 + fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s not addable", 1.2001 + __FILE__, __LINE__, fldName(field)); 1.2002 +#endif 1.2003 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2004 + return; 1.2005 + // throw new IllegalArgumentException("Calendar.add(" + fieldName(field) + 1.2006 + // ") not supported"); 1.2007 + } 1.2008 + 1.2009 + // In order to keep the hour invariant (for fields where this is 1.2010 + // appropriate), check the combined DST & ZONE offset before and 1.2011 + // after the add() operation. If it changes, then adjust the millis 1.2012 + // to compensate. 1.2013 + int32_t prevOffset = 0; 1.2014 + int32_t hour = 0; 1.2015 + if (keepHourInvariant) { 1.2016 + prevOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status); 1.2017 + hour = internalGet(UCAL_HOUR_OF_DAY); 1.2018 + } 1.2019 + 1.2020 + setTimeInMillis(getTimeInMillis(status) + delta, status); 1.2021 + 1.2022 + if (keepHourInvariant) { 1.2023 + int32_t newOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status); 1.2024 + if (newOffset != prevOffset) { 1.2025 + // We have done an hour-invariant adjustment but the 1.2026 + // combined offset has changed. We adjust millis to keep 1.2027 + // the hour constant. In cases such as midnight after 1.2028 + // a DST change which occurs at midnight, there is the 1.2029 + // danger of adjusting into a different day. To avoid 1.2030 + // this we make the adjustment only if it actually 1.2031 + // maintains the hour. 1.2032 + 1.2033 + // When the difference of the previous UTC offset and 1.2034 + // the new UTC offset exceeds 1 full day, we do not want 1.2035 + // to roll over/back the date. For now, this only happens 1.2036 + // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452. 1.2037 + int32_t adjAmount = prevOffset - newOffset; 1.2038 + adjAmount = adjAmount >= 0 ? adjAmount % (int32_t)kOneDay : -(-adjAmount % (int32_t)kOneDay); 1.2039 + if (adjAmount != 0) { 1.2040 + double t = internalGetTime(); 1.2041 + setTimeInMillis(t + adjAmount, status); 1.2042 + if (get(UCAL_HOUR_OF_DAY, status) != hour) { 1.2043 + setTimeInMillis(t, status); 1.2044 + } 1.2045 + } 1.2046 + } 1.2047 + } 1.2048 +} 1.2049 + 1.2050 +// ------------------------------------- 1.2051 +int32_t Calendar::fieldDifference(UDate when, EDateFields field, UErrorCode& status) { 1.2052 + return fieldDifference(when, (UCalendarDateFields) field, status); 1.2053 +} 1.2054 + 1.2055 +int32_t Calendar::fieldDifference(UDate targetMs, UCalendarDateFields field, UErrorCode& ec) { 1.2056 + if (U_FAILURE(ec)) return 0; 1.2057 + int32_t min = 0; 1.2058 + double startMs = getTimeInMillis(ec); 1.2059 + // Always add from the start millis. This accomodates 1.2060 + // operations like adding years from February 29, 2000 up to 1.2061 + // February 29, 2004. If 1, 1, 1, 1 is added to the year 1.2062 + // field, the DOM gets pinned to 28 and stays there, giving an 1.2063 + // incorrect DOM difference of 1. We have to add 1, reset, 2, 1.2064 + // reset, 3, reset, 4. 1.2065 + if (startMs < targetMs) { 1.2066 + int32_t max = 1; 1.2067 + // Find a value that is too large 1.2068 + while (U_SUCCESS(ec)) { 1.2069 + setTimeInMillis(startMs, ec); 1.2070 + add(field, max, ec); 1.2071 + double ms = getTimeInMillis(ec); 1.2072 + if (ms == targetMs) { 1.2073 + return max; 1.2074 + } else if (ms > targetMs) { 1.2075 + break; 1.2076 + } else if (max < INT32_MAX) { 1.2077 + min = max; 1.2078 + max <<= 1; 1.2079 + if (max < 0) { 1.2080 + max = INT32_MAX; 1.2081 + } 1.2082 + } else { 1.2083 + // Field difference too large to fit into int32_t 1.2084 +#if defined (U_DEBUG_CAL) 1.2085 + fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n", 1.2086 + __FILE__, __LINE__, fldName(field)); 1.2087 +#endif 1.2088 + ec = U_ILLEGAL_ARGUMENT_ERROR; 1.2089 + } 1.2090 + } 1.2091 + // Do a binary search 1.2092 + while ((max - min) > 1 && U_SUCCESS(ec)) { 1.2093 + int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX 1.2094 + setTimeInMillis(startMs, ec); 1.2095 + add(field, t, ec); 1.2096 + double ms = getTimeInMillis(ec); 1.2097 + if (ms == targetMs) { 1.2098 + return t; 1.2099 + } else if (ms > targetMs) { 1.2100 + max = t; 1.2101 + } else { 1.2102 + min = t; 1.2103 + } 1.2104 + } 1.2105 + } else if (startMs > targetMs) { 1.2106 + int32_t max = -1; 1.2107 + // Find a value that is too small 1.2108 + while (U_SUCCESS(ec)) { 1.2109 + setTimeInMillis(startMs, ec); 1.2110 + add(field, max, ec); 1.2111 + double ms = getTimeInMillis(ec); 1.2112 + if (ms == targetMs) { 1.2113 + return max; 1.2114 + } else if (ms < targetMs) { 1.2115 + break; 1.2116 + } else { 1.2117 + min = max; 1.2118 + max <<= 1; 1.2119 + if (max == 0) { 1.2120 + // Field difference too large to fit into int32_t 1.2121 +#if defined (U_DEBUG_CAL) 1.2122 + fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n", 1.2123 + __FILE__, __LINE__, fldName(field)); 1.2124 +#endif 1.2125 + ec = U_ILLEGAL_ARGUMENT_ERROR; 1.2126 + } 1.2127 + } 1.2128 + } 1.2129 + // Do a binary search 1.2130 + while ((min - max) > 1 && U_SUCCESS(ec)) { 1.2131 + int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX 1.2132 + setTimeInMillis(startMs, ec); 1.2133 + add(field, t, ec); 1.2134 + double ms = getTimeInMillis(ec); 1.2135 + if (ms == targetMs) { 1.2136 + return t; 1.2137 + } else if (ms < targetMs) { 1.2138 + max = t; 1.2139 + } else { 1.2140 + min = t; 1.2141 + } 1.2142 + } 1.2143 + } 1.2144 + // Set calendar to end point 1.2145 + setTimeInMillis(startMs, ec); 1.2146 + add(field, min, ec); 1.2147 + 1.2148 + /* Test for buffer overflows */ 1.2149 + if(U_FAILURE(ec)) { 1.2150 + return 0; 1.2151 + } 1.2152 + return min; 1.2153 +} 1.2154 + 1.2155 +// ------------------------------------- 1.2156 + 1.2157 +void 1.2158 +Calendar::adoptTimeZone(TimeZone* zone) 1.2159 +{ 1.2160 + // Do nothing if passed-in zone is NULL 1.2161 + if (zone == NULL) return; 1.2162 + 1.2163 + // fZone should always be non-null 1.2164 + if (fZone != NULL) delete fZone; 1.2165 + fZone = zone; 1.2166 + 1.2167 + // if the zone changes, we need to recompute the time fields 1.2168 + fAreFieldsSet = FALSE; 1.2169 +} 1.2170 + 1.2171 +// ------------------------------------- 1.2172 +void 1.2173 +Calendar::setTimeZone(const TimeZone& zone) 1.2174 +{ 1.2175 + adoptTimeZone(zone.clone()); 1.2176 +} 1.2177 + 1.2178 +// ------------------------------------- 1.2179 + 1.2180 +const TimeZone& 1.2181 +Calendar::getTimeZone() const 1.2182 +{ 1.2183 + return *fZone; 1.2184 +} 1.2185 + 1.2186 +// ------------------------------------- 1.2187 + 1.2188 +TimeZone* 1.2189 +Calendar::orphanTimeZone() 1.2190 +{ 1.2191 + TimeZone *z = fZone; 1.2192 + // we let go of the time zone; the new time zone is the system default time zone 1.2193 + fZone = TimeZone::createDefault(); 1.2194 + return z; 1.2195 +} 1.2196 + 1.2197 +// ------------------------------------- 1.2198 + 1.2199 +void 1.2200 +Calendar::setLenient(UBool lenient) 1.2201 +{ 1.2202 + fLenient = lenient; 1.2203 +} 1.2204 + 1.2205 +// ------------------------------------- 1.2206 + 1.2207 +UBool 1.2208 +Calendar::isLenient() const 1.2209 +{ 1.2210 + return fLenient; 1.2211 +} 1.2212 + 1.2213 +// ------------------------------------- 1.2214 + 1.2215 +void 1.2216 +Calendar::setRepeatedWallTimeOption(UCalendarWallTimeOption option) 1.2217 +{ 1.2218 + if (option == UCAL_WALLTIME_LAST || option == UCAL_WALLTIME_FIRST) { 1.2219 + fRepeatedWallTime = option; 1.2220 + } 1.2221 +} 1.2222 + 1.2223 +// ------------------------------------- 1.2224 + 1.2225 +UCalendarWallTimeOption 1.2226 +Calendar::getRepeatedWallTimeOption(void) const 1.2227 +{ 1.2228 + return fRepeatedWallTime; 1.2229 +} 1.2230 + 1.2231 +// ------------------------------------- 1.2232 + 1.2233 +void 1.2234 +Calendar::setSkippedWallTimeOption(UCalendarWallTimeOption option) 1.2235 +{ 1.2236 + fSkippedWallTime = option; 1.2237 +} 1.2238 + 1.2239 +// ------------------------------------- 1.2240 + 1.2241 +UCalendarWallTimeOption 1.2242 +Calendar::getSkippedWallTimeOption(void) const 1.2243 +{ 1.2244 + return fSkippedWallTime; 1.2245 +} 1.2246 + 1.2247 +// ------------------------------------- 1.2248 + 1.2249 +void 1.2250 +Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value) 1.2251 +{ 1.2252 + if (fFirstDayOfWeek != value && 1.2253 + value >= UCAL_SUNDAY && value <= UCAL_SATURDAY) { 1.2254 + fFirstDayOfWeek = value; 1.2255 + fAreFieldsSet = FALSE; 1.2256 + } 1.2257 +} 1.2258 + 1.2259 +// ------------------------------------- 1.2260 + 1.2261 +Calendar::EDaysOfWeek 1.2262 +Calendar::getFirstDayOfWeek() const 1.2263 +{ 1.2264 + return (Calendar::EDaysOfWeek)fFirstDayOfWeek; 1.2265 +} 1.2266 + 1.2267 +UCalendarDaysOfWeek 1.2268 +Calendar::getFirstDayOfWeek(UErrorCode & /*status*/) const 1.2269 +{ 1.2270 + return fFirstDayOfWeek; 1.2271 +} 1.2272 +// ------------------------------------- 1.2273 + 1.2274 +void 1.2275 +Calendar::setMinimalDaysInFirstWeek(uint8_t value) 1.2276 +{ 1.2277 + // Values less than 1 have the same effect as 1; values greater 1.2278 + // than 7 have the same effect as 7. However, we normalize values 1.2279 + // so operator== and so forth work. 1.2280 + if (value < 1) { 1.2281 + value = 1; 1.2282 + } else if (value > 7) { 1.2283 + value = 7; 1.2284 + } 1.2285 + if (fMinimalDaysInFirstWeek != value) { 1.2286 + fMinimalDaysInFirstWeek = value; 1.2287 + fAreFieldsSet = FALSE; 1.2288 + } 1.2289 +} 1.2290 + 1.2291 +// ------------------------------------- 1.2292 + 1.2293 +uint8_t 1.2294 +Calendar::getMinimalDaysInFirstWeek() const 1.2295 +{ 1.2296 + return fMinimalDaysInFirstWeek; 1.2297 +} 1.2298 + 1.2299 +// ------------------------------------- 1.2300 +// weekend functions, just dummy implementations for now (for API freeze) 1.2301 + 1.2302 +UCalendarWeekdayType 1.2303 +Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const 1.2304 +{ 1.2305 + if (U_FAILURE(status)) { 1.2306 + return UCAL_WEEKDAY; 1.2307 + } 1.2308 + if (dayOfWeek < UCAL_SUNDAY || dayOfWeek > UCAL_SATURDAY) { 1.2309 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2310 + return UCAL_WEEKDAY; 1.2311 + } 1.2312 + if (fWeekendOnset == fWeekendCease) { 1.2313 + if (dayOfWeek != fWeekendOnset) 1.2314 + return UCAL_WEEKDAY; 1.2315 + return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET; 1.2316 + } 1.2317 + if (fWeekendOnset < fWeekendCease) { 1.2318 + if (dayOfWeek < fWeekendOnset || dayOfWeek > fWeekendCease) { 1.2319 + return UCAL_WEEKDAY; 1.2320 + } 1.2321 + } else { 1.2322 + if (dayOfWeek > fWeekendCease && dayOfWeek < fWeekendOnset) { 1.2323 + return UCAL_WEEKDAY; 1.2324 + } 1.2325 + } 1.2326 + if (dayOfWeek == fWeekendOnset) { 1.2327 + return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET; 1.2328 + } 1.2329 + if (dayOfWeek == fWeekendCease) { 1.2330 + return (fWeekendCeaseMillis >= 86400000) ? UCAL_WEEKEND : UCAL_WEEKEND_CEASE; 1.2331 + } 1.2332 + return UCAL_WEEKEND; 1.2333 +} 1.2334 + 1.2335 +int32_t 1.2336 +Calendar::getWeekendTransition(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const 1.2337 +{ 1.2338 + if (U_FAILURE(status)) { 1.2339 + return 0; 1.2340 + } 1.2341 + if (dayOfWeek == fWeekendOnset) { 1.2342 + return fWeekendOnsetMillis; 1.2343 + } else if (dayOfWeek == fWeekendCease) { 1.2344 + return fWeekendCeaseMillis; 1.2345 + } 1.2346 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2347 + return 0; 1.2348 +} 1.2349 + 1.2350 +UBool 1.2351 +Calendar::isWeekend(UDate date, UErrorCode &status) const 1.2352 +{ 1.2353 + if (U_FAILURE(status)) { 1.2354 + return FALSE; 1.2355 + } 1.2356 + // clone the calendar so we don't mess with the real one. 1.2357 + Calendar *work = (Calendar*)this->clone(); 1.2358 + if (work == NULL) { 1.2359 + status = U_MEMORY_ALLOCATION_ERROR; 1.2360 + return FALSE; 1.2361 + } 1.2362 + UBool result = FALSE; 1.2363 + work->setTime(date, status); 1.2364 + if (U_SUCCESS(status)) { 1.2365 + result = work->isWeekend(); 1.2366 + } 1.2367 + delete work; 1.2368 + return result; 1.2369 +} 1.2370 + 1.2371 +UBool 1.2372 +Calendar::isWeekend(void) const 1.2373 +{ 1.2374 + UErrorCode status = U_ZERO_ERROR; 1.2375 + UCalendarDaysOfWeek dayOfWeek = (UCalendarDaysOfWeek)get(UCAL_DAY_OF_WEEK, status); 1.2376 + UCalendarWeekdayType dayType = getDayOfWeekType(dayOfWeek, status); 1.2377 + if (U_SUCCESS(status)) { 1.2378 + switch (dayType) { 1.2379 + case UCAL_WEEKDAY: 1.2380 + return FALSE; 1.2381 + case UCAL_WEEKEND: 1.2382 + return TRUE; 1.2383 + case UCAL_WEEKEND_ONSET: 1.2384 + case UCAL_WEEKEND_CEASE: 1.2385 + // Use internalGet() because the above call to get() populated all fields. 1.2386 + { 1.2387 + int32_t millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY); 1.2388 + int32_t transitionMillis = getWeekendTransition(dayOfWeek, status); 1.2389 + if (U_SUCCESS(status)) { 1.2390 + return (dayType == UCAL_WEEKEND_ONSET)? 1.2391 + (millisInDay >= transitionMillis): 1.2392 + (millisInDay < transitionMillis); 1.2393 + } 1.2394 + // else fall through, return FALSE 1.2395 + } 1.2396 + default: 1.2397 + break; 1.2398 + } 1.2399 + } 1.2400 + return FALSE; 1.2401 +} 1.2402 + 1.2403 +// ------------------------------------- limits 1.2404 + 1.2405 +int32_t 1.2406 +Calendar::getMinimum(EDateFields field) const { 1.2407 + return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MINIMUM); 1.2408 +} 1.2409 + 1.2410 +int32_t 1.2411 +Calendar::getMinimum(UCalendarDateFields field) const 1.2412 +{ 1.2413 + return getLimit(field,UCAL_LIMIT_MINIMUM); 1.2414 +} 1.2415 + 1.2416 +// ------------------------------------- 1.2417 +int32_t 1.2418 +Calendar::getMaximum(EDateFields field) const 1.2419 +{ 1.2420 + return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MAXIMUM); 1.2421 +} 1.2422 + 1.2423 +int32_t 1.2424 +Calendar::getMaximum(UCalendarDateFields field) const 1.2425 +{ 1.2426 + return getLimit(field,UCAL_LIMIT_MAXIMUM); 1.2427 +} 1.2428 + 1.2429 +// ------------------------------------- 1.2430 +int32_t 1.2431 +Calendar::getGreatestMinimum(EDateFields field) const 1.2432 +{ 1.2433 + return getLimit((UCalendarDateFields)field,UCAL_LIMIT_GREATEST_MINIMUM); 1.2434 +} 1.2435 + 1.2436 +int32_t 1.2437 +Calendar::getGreatestMinimum(UCalendarDateFields field) const 1.2438 +{ 1.2439 + return getLimit(field,UCAL_LIMIT_GREATEST_MINIMUM); 1.2440 +} 1.2441 + 1.2442 +// ------------------------------------- 1.2443 +int32_t 1.2444 +Calendar::getLeastMaximum(EDateFields field) const 1.2445 +{ 1.2446 + return getLimit((UCalendarDateFields) field,UCAL_LIMIT_LEAST_MAXIMUM); 1.2447 +} 1.2448 + 1.2449 +int32_t 1.2450 +Calendar::getLeastMaximum(UCalendarDateFields field) const 1.2451 +{ 1.2452 + return getLimit( field,UCAL_LIMIT_LEAST_MAXIMUM); 1.2453 +} 1.2454 + 1.2455 +// ------------------------------------- 1.2456 +int32_t 1.2457 +Calendar::getActualMinimum(EDateFields field, UErrorCode& status) const 1.2458 +{ 1.2459 + return getActualMinimum((UCalendarDateFields) field, status); 1.2460 +} 1.2461 + 1.2462 +int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) const { 1.2463 + switch (field) { 1.2464 + case UCAL_DAY_OF_WEEK: 1.2465 + case UCAL_AM_PM: 1.2466 + case UCAL_HOUR: 1.2467 + case UCAL_HOUR_OF_DAY: 1.2468 + case UCAL_MINUTE: 1.2469 + case UCAL_SECOND: 1.2470 + case UCAL_MILLISECOND: 1.2471 + case UCAL_ZONE_OFFSET: 1.2472 + case UCAL_DST_OFFSET: 1.2473 + case UCAL_DOW_LOCAL: 1.2474 + case UCAL_JULIAN_DAY: 1.2475 + case UCAL_MILLISECONDS_IN_DAY: 1.2476 + case UCAL_IS_LEAP_MONTH: 1.2477 + return kCalendarLimits[field][limitType]; 1.2478 + 1.2479 + case UCAL_WEEK_OF_MONTH: 1.2480 + { 1.2481 + int32_t limit; 1.2482 + if (limitType == UCAL_LIMIT_MINIMUM) { 1.2483 + limit = getMinimalDaysInFirstWeek() == 1 ? 1 : 0; 1.2484 + } else if (limitType == UCAL_LIMIT_GREATEST_MINIMUM) { 1.2485 + limit = 1; 1.2486 + } else { 1.2487 + int32_t minDaysInFirst = getMinimalDaysInFirstWeek(); 1.2488 + int32_t daysInMonth = handleGetLimit(UCAL_DAY_OF_MONTH, limitType); 1.2489 + if (limitType == UCAL_LIMIT_LEAST_MAXIMUM) { 1.2490 + limit = (daysInMonth + (7 - minDaysInFirst)) / 7; 1.2491 + } else { // limitType == UCAL_LIMIT_MAXIMUM 1.2492 + limit = (daysInMonth + 6 + (7 - minDaysInFirst)) / 7; 1.2493 + } 1.2494 + } 1.2495 + return limit; 1.2496 + } 1.2497 + default: 1.2498 + return handleGetLimit(field, limitType); 1.2499 + } 1.2500 +} 1.2501 + 1.2502 + 1.2503 +int32_t 1.2504 +Calendar::getActualMinimum(UCalendarDateFields field, UErrorCode& status) const 1.2505 +{ 1.2506 + int32_t fieldValue = getGreatestMinimum(field); 1.2507 + int32_t endValue = getMinimum(field); 1.2508 + 1.2509 + // if we know that the minimum value is always the same, just return it 1.2510 + if (fieldValue == endValue) { 1.2511 + return fieldValue; 1.2512 + } 1.2513 + 1.2514 + // clone the calendar so we don't mess with the real one, and set it to 1.2515 + // accept anything for the field values 1.2516 + Calendar *work = (Calendar*)this->clone(); 1.2517 + if (work == NULL) { 1.2518 + status = U_MEMORY_ALLOCATION_ERROR; 1.2519 + return 0; 1.2520 + } 1.2521 + work->setLenient(TRUE); 1.2522 + 1.2523 + // now try each value from getLeastMaximum() to getMaximum() one by one until 1.2524 + // we get a value that normalizes to another value. The last value that 1.2525 + // normalizes to itself is the actual minimum for the current date 1.2526 + int32_t result = fieldValue; 1.2527 + 1.2528 + do { 1.2529 + work->set(field, fieldValue); 1.2530 + if (work->get(field, status) != fieldValue) { 1.2531 + break; 1.2532 + } 1.2533 + else { 1.2534 + result = fieldValue; 1.2535 + fieldValue--; 1.2536 + } 1.2537 + } while (fieldValue >= endValue); 1.2538 + 1.2539 + delete work; 1.2540 + 1.2541 + /* Test for buffer overflows */ 1.2542 + if(U_FAILURE(status)) { 1.2543 + return 0; 1.2544 + } 1.2545 + return result; 1.2546 +} 1.2547 + 1.2548 +// ------------------------------------- 1.2549 + 1.2550 + 1.2551 + 1.2552 +/** 1.2553 +* Ensure that each field is within its valid range by calling {@link 1.2554 +* #validateField(int)} on each field that has been set. This method 1.2555 +* should only be called if this calendar is not lenient. 1.2556 +* @see #isLenient 1.2557 +* @see #validateField(int) 1.2558 +*/ 1.2559 +void Calendar::validateFields(UErrorCode &status) { 1.2560 + for (int32_t field = 0; U_SUCCESS(status) && (field < UCAL_FIELD_COUNT); field++) { 1.2561 + if (fStamp[field] >= kMinimumUserStamp) { 1.2562 + validateField((UCalendarDateFields)field, status); 1.2563 + } 1.2564 + } 1.2565 +} 1.2566 + 1.2567 +/** 1.2568 +* Validate a single field of this calendar. Subclasses should 1.2569 +* override this method to validate any calendar-specific fields. 1.2570 +* Generic fields can be handled by 1.2571 +* <code>Calendar.validateField()</code>. 1.2572 +* @see #validateField(int, int, int) 1.2573 +*/ 1.2574 +void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) { 1.2575 + int32_t y; 1.2576 + switch (field) { 1.2577 + case UCAL_DAY_OF_MONTH: 1.2578 + y = handleGetExtendedYear(); 1.2579 + validateField(field, 1, handleGetMonthLength(y, internalGet(UCAL_MONTH)), status); 1.2580 + break; 1.2581 + case UCAL_DAY_OF_YEAR: 1.2582 + y = handleGetExtendedYear(); 1.2583 + validateField(field, 1, handleGetYearLength(y), status); 1.2584 + break; 1.2585 + case UCAL_DAY_OF_WEEK_IN_MONTH: 1.2586 + if (internalGet(field) == 0) { 1.2587 +#if defined (U_DEBUG_CAL) 1.2588 + fprintf(stderr, "%s:%d: ILLEGAL ARG because DOW in month cannot be 0\n", 1.2589 + __FILE__, __LINE__); 1.2590 +#endif 1.2591 + status = U_ILLEGAL_ARGUMENT_ERROR; // "DAY_OF_WEEK_IN_MONTH cannot be zero" 1.2592 + return; 1.2593 + } 1.2594 + validateField(field, getMinimum(field), getMaximum(field), status); 1.2595 + break; 1.2596 + default: 1.2597 + validateField(field, getMinimum(field), getMaximum(field), status); 1.2598 + break; 1.2599 + } 1.2600 +} 1.2601 + 1.2602 +/** 1.2603 +* Validate a single field of this calendar given its minimum and 1.2604 +* maximum allowed value. If the field is out of range, throw a 1.2605 +* descriptive <code>IllegalArgumentException</code>. Subclasses may 1.2606 +* use this method in their implementation of {@link 1.2607 +* #validateField(int)}. 1.2608 +*/ 1.2609 +void Calendar::validateField(UCalendarDateFields field, int32_t min, int32_t max, UErrorCode& status) 1.2610 +{ 1.2611 + int32_t value = fFields[field]; 1.2612 + if (value < min || value > max) { 1.2613 +#if defined (U_DEBUG_CAL) 1.2614 + fprintf(stderr, "%s:%d: ILLEGAL ARG because of field %s out of range %d..%d at %d\n", 1.2615 + __FILE__, __LINE__,fldName(field),min,max,value); 1.2616 +#endif 1.2617 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2618 + return; 1.2619 + } 1.2620 +} 1.2621 + 1.2622 +// ------------------------- 1.2623 + 1.2624 +const UFieldResolutionTable* Calendar::getFieldResolutionTable() const { 1.2625 + return kDatePrecedence; 1.2626 +} 1.2627 + 1.2628 + 1.2629 +UCalendarDateFields Calendar::newerField(UCalendarDateFields defaultField, UCalendarDateFields alternateField) const 1.2630 +{ 1.2631 + if (fStamp[alternateField] > fStamp[defaultField]) { 1.2632 + return alternateField; 1.2633 + } 1.2634 + return defaultField; 1.2635 +} 1.2636 + 1.2637 +UCalendarDateFields Calendar::resolveFields(const UFieldResolutionTable* precedenceTable) { 1.2638 + int32_t bestField = UCAL_FIELD_COUNT; 1.2639 + int32_t tempBestField; 1.2640 + for (int32_t g=0; precedenceTable[g][0][0] != -1 && (bestField == UCAL_FIELD_COUNT); ++g) { 1.2641 + int32_t bestStamp = kUnset; 1.2642 + for (int32_t l=0; precedenceTable[g][l][0] != -1; ++l) { 1.2643 + int32_t lineStamp = kUnset; 1.2644 + // Skip over first entry if it is negative 1.2645 + for (int32_t i=((precedenceTable[g][l][0]>=kResolveRemap)?1:0); precedenceTable[g][l][i]!=-1; ++i) { 1.2646 + U_ASSERT(precedenceTable[g][l][i] < UCAL_FIELD_COUNT); 1.2647 + int32_t s = fStamp[precedenceTable[g][l][i]]; 1.2648 + // If any field is unset then don't use this line 1.2649 + if (s == kUnset) { 1.2650 + goto linesInGroup; 1.2651 + } else if(s > lineStamp) { 1.2652 + lineStamp = s; 1.2653 + } 1.2654 + } 1.2655 + // Record new maximum stamp & field no. 1.2656 + if (lineStamp > bestStamp) { 1.2657 + tempBestField = precedenceTable[g][l][0]; // First field refers to entire line 1.2658 + if (tempBestField >= kResolveRemap) { 1.2659 + tempBestField &= (kResolveRemap-1); 1.2660 + // This check is needed to resolve some issues with UCAL_YEAR precedence mapping 1.2661 + if (tempBestField != UCAL_DATE || (fStamp[UCAL_WEEK_OF_MONTH] < fStamp[tempBestField])) { 1.2662 + bestField = tempBestField; 1.2663 + } 1.2664 + } else { 1.2665 + bestField = tempBestField; 1.2666 + } 1.2667 + 1.2668 + if (bestField == tempBestField) { 1.2669 + bestStamp = lineStamp; 1.2670 + } 1.2671 + } 1.2672 +linesInGroup: 1.2673 + ; 1.2674 + } 1.2675 + } 1.2676 + return (UCalendarDateFields)bestField; 1.2677 +} 1.2678 + 1.2679 +const UFieldResolutionTable Calendar::kDatePrecedence[] = 1.2680 +{ 1.2681 + { 1.2682 + { UCAL_DAY_OF_MONTH, kResolveSTOP }, 1.2683 + { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP }, 1.2684 + { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 1.2685 + { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 1.2686 + { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP }, 1.2687 + { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 1.2688 + { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 1.2689 + { UCAL_DAY_OF_YEAR, kResolveSTOP }, 1.2690 + { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_YEAR, kResolveSTOP }, // if YEAR is set over YEAR_WOY use DAY_OF_MONTH 1.2691 + { kResolveRemap | UCAL_WEEK_OF_YEAR, UCAL_YEAR_WOY, kResolveSTOP }, // if YEAR_WOY is set, calc based on WEEK_OF_YEAR 1.2692 + { kResolveSTOP } 1.2693 + }, 1.2694 + { 1.2695 + { UCAL_WEEK_OF_YEAR, kResolveSTOP }, 1.2696 + { UCAL_WEEK_OF_MONTH, kResolveSTOP }, 1.2697 + { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP }, 1.2698 + { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP }, 1.2699 + { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP }, 1.2700 + { kResolveSTOP } 1.2701 + }, 1.2702 + {{kResolveSTOP}} 1.2703 +}; 1.2704 + 1.2705 + 1.2706 +const UFieldResolutionTable Calendar::kDOWPrecedence[] = 1.2707 +{ 1.2708 + { 1.2709 + { UCAL_DAY_OF_WEEK,kResolveSTOP, kResolveSTOP }, 1.2710 + { UCAL_DOW_LOCAL,kResolveSTOP, kResolveSTOP }, 1.2711 + {kResolveSTOP} 1.2712 + }, 1.2713 + {{kResolveSTOP}} 1.2714 +}; 1.2715 + 1.2716 +// precedence for calculating a year 1.2717 +const UFieldResolutionTable Calendar::kYearPrecedence[] = 1.2718 +{ 1.2719 + { 1.2720 + { UCAL_YEAR, kResolveSTOP }, 1.2721 + { UCAL_EXTENDED_YEAR, kResolveSTOP }, 1.2722 + { UCAL_YEAR_WOY, UCAL_WEEK_OF_YEAR, kResolveSTOP }, // YEAR_WOY is useless without WEEK_OF_YEAR 1.2723 + { kResolveSTOP } 1.2724 + }, 1.2725 + {{kResolveSTOP}} 1.2726 +}; 1.2727 + 1.2728 + 1.2729 +// ------------------------- 1.2730 + 1.2731 + 1.2732 +void Calendar::computeTime(UErrorCode& status) { 1.2733 + if (!isLenient()) { 1.2734 + validateFields(status); 1.2735 + if (U_FAILURE(status)) { 1.2736 + return; 1.2737 + } 1.2738 + } 1.2739 + 1.2740 + // Compute the Julian day 1.2741 + int32_t julianDay = computeJulianDay(); 1.2742 + 1.2743 + double millis = Grego::julianDayToMillis(julianDay); 1.2744 + 1.2745 +#if defined (U_DEBUG_CAL) 1.2746 + // int32_t julianInsanityCheck = (int32_t)ClockMath::floorDivide(millis, kOneDay); 1.2747 + // julianInsanityCheck += kEpochStartAsJulianDay; 1.2748 + // if(1 || julianInsanityCheck != julianDay) { 1.2749 + // fprintf(stderr, "%s:%d- D'oh- computed jules %d, to mills (%s)%.lf, recomputed %d\n", 1.2750 + // __FILE__, __LINE__, julianDay, millis<0.0?"NEG":"", millis, julianInsanityCheck); 1.2751 + // } 1.2752 +#endif 1.2753 + 1.2754 + int32_t millisInDay; 1.2755 + 1.2756 + // We only use MILLISECONDS_IN_DAY if it has been set by the user. 1.2757 + // This makes it possible for the caller to set the calendar to a 1.2758 + // time and call clear(MONTH) to reset the MONTH to January. This 1.2759 + // is legacy behavior. Without this, clear(MONTH) has no effect, 1.2760 + // since the internally set JULIAN_DAY is used. 1.2761 + if (fStamp[UCAL_MILLISECONDS_IN_DAY] >= ((int32_t)kMinimumUserStamp) && 1.2762 + newestStamp(UCAL_AM_PM, UCAL_MILLISECOND, kUnset) <= fStamp[UCAL_MILLISECONDS_IN_DAY]) { 1.2763 + millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY); 1.2764 + } else { 1.2765 + millisInDay = computeMillisInDay(); 1.2766 + } 1.2767 + 1.2768 + UDate t = 0; 1.2769 + if (fStamp[UCAL_ZONE_OFFSET] >= ((int32_t)kMinimumUserStamp) || fStamp[UCAL_DST_OFFSET] >= ((int32_t)kMinimumUserStamp)) { 1.2770 + t = millis + millisInDay - (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET)); 1.2771 + } else { 1.2772 + // Compute the time zone offset and DST offset. There are two potential 1.2773 + // ambiguities here. We'll assume a 2:00 am (wall time) switchover time 1.2774 + // for discussion purposes here. 1.2775 + // 1.2776 + // 1. The positive offset change such as transition into DST. 1.2777 + // Here, a designated time of 2:00 am - 2:59 am does not actually exist. 1.2778 + // For this case, skippedWallTime option specifies the behavior. 1.2779 + // For example, 2:30 am is interpreted as; 1.2780 + // - WALLTIME_LAST(default): 3:30 am (DST) (interpreting 2:30 am as 31 minutes after 1:59 am (STD)) 1.2781 + // - WALLTIME_FIRST: 1:30 am (STD) (interpreting 2:30 am as 30 minutes before 3:00 am (DST)) 1.2782 + // - WALLTIME_NEXT_VALID: 3:00 am (DST) (next valid time after 2:30 am on a wall clock) 1.2783 + // 2. The negative offset change such as transition out of DST. 1.2784 + // Here, a designated time of 1:00 am - 1:59 am can be in standard or DST. Both are valid 1.2785 + // representations (the rep jumps from 1:59:59 DST to 1:00:00 Std). 1.2786 + // For this case, repeatedWallTime option specifies the behavior. 1.2787 + // For example, 1:30 am is interpreted as; 1.2788 + // - WALLTIME_LAST(default): 1:30 am (STD) - latter occurrence 1.2789 + // - WALLTIME_FIRST: 1:30 am (DST) - former occurrence 1.2790 + // 1.2791 + // In addition to above, when calendar is strict (not default), wall time falls into 1.2792 + // the skipped time range will be processed as an error case. 1.2793 + // 1.2794 + // These special cases are mostly handled in #computeZoneOffset(long), except WALLTIME_NEXT_VALID 1.2795 + // at positive offset change. The protected method computeZoneOffset(long) is exposed to Calendar 1.2796 + // subclass implementations and marked as @stable. Strictly speaking, WALLTIME_NEXT_VALID 1.2797 + // should be also handled in the same place, but we cannot change the code flow without deprecating 1.2798 + // the protected method. 1.2799 + // 1.2800 + // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET 1.2801 + // or DST_OFFSET fields; then we use those fields. 1.2802 + 1.2803 + if (!isLenient() || fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID) { 1.2804 + // When strict, invalidate a wall time falls into a skipped wall time range. 1.2805 + // When lenient and skipped wall time option is WALLTIME_NEXT_VALID, 1.2806 + // the result time will be adjusted to the next valid time (on wall clock). 1.2807 + int32_t zoneOffset = computeZoneOffset(millis, millisInDay, status); 1.2808 + UDate tmpTime = millis + millisInDay - zoneOffset; 1.2809 + 1.2810 + int32_t raw, dst; 1.2811 + fZone->getOffset(tmpTime, FALSE, raw, dst, status); 1.2812 + 1.2813 + if (U_SUCCESS(status)) { 1.2814 + // zoneOffset != (raw + dst) only when the given wall time fall into 1.2815 + // a skipped wall time range caused by positive zone offset transition. 1.2816 + if (zoneOffset != (raw + dst)) { 1.2817 + if (!isLenient()) { 1.2818 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.2819 + } else { 1.2820 + U_ASSERT(fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID); 1.2821 + // Adjust time to the next valid wall clock time. 1.2822 + // At this point, tmpTime is on or after the zone offset transition causing 1.2823 + // the skipped time range. 1.2824 + 1.2825 + BasicTimeZone *btz = getBasicTimeZone(); 1.2826 + if (btz) { 1.2827 + TimeZoneTransition transition; 1.2828 + UBool hasTransition = btz->getPreviousTransition(tmpTime, TRUE, transition); 1.2829 + if (hasTransition) { 1.2830 + t = transition.getTime(); 1.2831 + } else { 1.2832 + // Could not find any transitions. 1.2833 + // Note: This should never happen. 1.2834 + status = U_INTERNAL_PROGRAM_ERROR; 1.2835 + } 1.2836 + } else { 1.2837 + // If not BasicTimeZone, return unsupported error for now. 1.2838 + // TODO: We may support non-BasicTimeZone in future. 1.2839 + status = U_UNSUPPORTED_ERROR; 1.2840 + } 1.2841 + } 1.2842 + } else { 1.2843 + t = tmpTime; 1.2844 + } 1.2845 + } 1.2846 + } else { 1.2847 + t = millis + millisInDay - computeZoneOffset(millis, millisInDay, status); 1.2848 + } 1.2849 + } 1.2850 + if (U_SUCCESS(status)) { 1.2851 + internalSetTime(t); 1.2852 + } 1.2853 +} 1.2854 + 1.2855 +/** 1.2856 +* Compute the milliseconds in the day from the fields. This is a 1.2857 +* value from 0 to 23:59:59.999 inclusive, unless fields are out of 1.2858 +* range, in which case it can be an arbitrary value. This value 1.2859 +* reflects local zone wall time. 1.2860 +* @stable ICU 2.0 1.2861 +*/ 1.2862 +int32_t Calendar::computeMillisInDay() { 1.2863 + // Do the time portion of the conversion. 1.2864 + 1.2865 + int32_t millisInDay = 0; 1.2866 + 1.2867 + // Find the best set of fields specifying the time of day. There 1.2868 + // are only two possibilities here; the HOUR_OF_DAY or the 1.2869 + // AM_PM and the HOUR. 1.2870 + int32_t hourOfDayStamp = fStamp[UCAL_HOUR_OF_DAY]; 1.2871 + int32_t hourStamp = (fStamp[UCAL_HOUR] > fStamp[UCAL_AM_PM])?fStamp[UCAL_HOUR]:fStamp[UCAL_AM_PM]; 1.2872 + int32_t bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp; 1.2873 + 1.2874 + // Hours 1.2875 + if (bestStamp != kUnset) { 1.2876 + if (bestStamp == hourOfDayStamp) { 1.2877 + // Don't normalize here; let overflow bump into the next period. 1.2878 + // This is consistent with how we handle other fields. 1.2879 + millisInDay += internalGet(UCAL_HOUR_OF_DAY); 1.2880 + } else { 1.2881 + // Don't normalize here; let overflow bump into the next period. 1.2882 + // This is consistent with how we handle other fields. 1.2883 + millisInDay += internalGet(UCAL_HOUR); 1.2884 + millisInDay += 12 * internalGet(UCAL_AM_PM); // Default works for unset AM_PM 1.2885 + } 1.2886 + } 1.2887 + 1.2888 + // We use the fact that unset == 0; we start with millisInDay 1.2889 + // == HOUR_OF_DAY. 1.2890 + millisInDay *= 60; 1.2891 + millisInDay += internalGet(UCAL_MINUTE); // now have minutes 1.2892 + millisInDay *= 60; 1.2893 + millisInDay += internalGet(UCAL_SECOND); // now have seconds 1.2894 + millisInDay *= 1000; 1.2895 + millisInDay += internalGet(UCAL_MILLISECOND); // now have millis 1.2896 + 1.2897 + return millisInDay; 1.2898 +} 1.2899 + 1.2900 +/** 1.2901 +* This method can assume EXTENDED_YEAR has been set. 1.2902 +* @param millis milliseconds of the date fields 1.2903 +* @param millisInDay milliseconds of the time fields; may be out 1.2904 +* or range. 1.2905 +* @stable ICU 2.0 1.2906 +*/ 1.2907 +int32_t Calendar::computeZoneOffset(double millis, int32_t millisInDay, UErrorCode &ec) { 1.2908 + int32_t rawOffset, dstOffset; 1.2909 + UDate wall = millis + millisInDay; 1.2910 + BasicTimeZone* btz = getBasicTimeZone(); 1.2911 + if (btz) { 1.2912 + int duplicatedTimeOpt = (fRepeatedWallTime == UCAL_WALLTIME_FIRST) ? BasicTimeZone::kFormer : BasicTimeZone::kLatter; 1.2913 + int nonExistingTimeOpt = (fSkippedWallTime == UCAL_WALLTIME_FIRST) ? BasicTimeZone::kLatter : BasicTimeZone::kFormer; 1.2914 + btz->getOffsetFromLocal(wall, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, ec); 1.2915 + } else { 1.2916 + const TimeZone& tz = getTimeZone(); 1.2917 + // By default, TimeZone::getOffset behaves UCAL_WALLTIME_LAST for both. 1.2918 + tz.getOffset(wall, TRUE, rawOffset, dstOffset, ec); 1.2919 + 1.2920 + UBool sawRecentNegativeShift = FALSE; 1.2921 + if (fRepeatedWallTime == UCAL_WALLTIME_FIRST) { 1.2922 + // Check if the given wall time falls into repeated time range 1.2923 + UDate tgmt = wall - (rawOffset + dstOffset); 1.2924 + 1.2925 + // Any negative zone transition within last 6 hours? 1.2926 + // Note: The maximum historic negative zone transition is -3 hours in the tz database. 1.2927 + // 6 hour window would be sufficient for this purpose. 1.2928 + int32_t tmpRaw, tmpDst; 1.2929 + tz.getOffset(tgmt - 6*60*60*1000, FALSE, tmpRaw, tmpDst, ec); 1.2930 + int32_t offsetDelta = (rawOffset + dstOffset) - (tmpRaw + tmpDst); 1.2931 + 1.2932 + U_ASSERT(offsetDelta < -6*60*60*1000); 1.2933 + if (offsetDelta < 0) { 1.2934 + sawRecentNegativeShift = TRUE; 1.2935 + // Negative shift within last 6 hours. When UCAL_WALLTIME_FIRST is used and the given wall time falls 1.2936 + // into the repeated time range, use offsets before the transition. 1.2937 + // Note: If it does not fall into the repeated time range, offsets remain unchanged below. 1.2938 + tz.getOffset(wall + offsetDelta, TRUE, rawOffset, dstOffset, ec); 1.2939 + } 1.2940 + } 1.2941 + if (!sawRecentNegativeShift && fSkippedWallTime == UCAL_WALLTIME_FIRST) { 1.2942 + // When skipped wall time option is WALLTIME_FIRST, 1.2943 + // recalculate offsets from the resolved time (non-wall). 1.2944 + // When the given wall time falls into skipped wall time, 1.2945 + // the offsets will be based on the zone offsets AFTER 1.2946 + // the transition (which means, earliest possibe interpretation). 1.2947 + UDate tgmt = wall - (rawOffset + dstOffset); 1.2948 + tz.getOffset(tgmt, FALSE, rawOffset, dstOffset, ec); 1.2949 + } 1.2950 + } 1.2951 + return rawOffset + dstOffset; 1.2952 +} 1.2953 + 1.2954 +int32_t Calendar::computeJulianDay() 1.2955 +{ 1.2956 + // We want to see if any of the date fields is newer than the 1.2957 + // JULIAN_DAY. If not, then we use JULIAN_DAY. If so, then we do 1.2958 + // the normal resolution. We only use JULIAN_DAY if it has been 1.2959 + // set by the user. This makes it possible for the caller to set 1.2960 + // the calendar to a time and call clear(MONTH) to reset the MONTH 1.2961 + // to January. This is legacy behavior. Without this, 1.2962 + // clear(MONTH) has no effect, since the internally set JULIAN_DAY 1.2963 + // is used. 1.2964 + if (fStamp[UCAL_JULIAN_DAY] >= (int32_t)kMinimumUserStamp) { 1.2965 + int32_t bestStamp = newestStamp(UCAL_ERA, UCAL_DAY_OF_WEEK_IN_MONTH, kUnset); 1.2966 + bestStamp = newestStamp(UCAL_YEAR_WOY, UCAL_EXTENDED_YEAR, bestStamp); 1.2967 + if (bestStamp <= fStamp[UCAL_JULIAN_DAY]) { 1.2968 + return internalGet(UCAL_JULIAN_DAY); 1.2969 + } 1.2970 + } 1.2971 + 1.2972 + UCalendarDateFields bestField = resolveFields(getFieldResolutionTable()); 1.2973 + if (bestField == UCAL_FIELD_COUNT) { 1.2974 + bestField = UCAL_DAY_OF_MONTH; 1.2975 + } 1.2976 + 1.2977 + return handleComputeJulianDay(bestField); 1.2978 +} 1.2979 + 1.2980 +// ------------------------------------------- 1.2981 + 1.2982 +int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField) { 1.2983 + UBool useMonth = (bestField == UCAL_DAY_OF_MONTH || 1.2984 + bestField == UCAL_WEEK_OF_MONTH || 1.2985 + bestField == UCAL_DAY_OF_WEEK_IN_MONTH); 1.2986 + int32_t year; 1.2987 + 1.2988 + if (bestField == UCAL_WEEK_OF_YEAR) { 1.2989 + year = internalGet(UCAL_YEAR_WOY, handleGetExtendedYear()); 1.2990 + internalSet(UCAL_EXTENDED_YEAR, year); 1.2991 + } else { 1.2992 + year = handleGetExtendedYear(); 1.2993 + internalSet(UCAL_EXTENDED_YEAR, year); 1.2994 + } 1.2995 + 1.2996 +#if defined (U_DEBUG_CAL) 1.2997 + fprintf(stderr, "%s:%d: bestField= %s - y=%d\n", __FILE__, __LINE__, fldName(bestField), year); 1.2998 +#endif 1.2999 + 1.3000 + // Get the Julian day of the day BEFORE the start of this year. 1.3001 + // If useMonth is true, get the day before the start of the month. 1.3002 + 1.3003 + // give calendar subclass a chance to have a default 'first' month 1.3004 + int32_t month; 1.3005 + 1.3006 + if(isSet(UCAL_MONTH)) { 1.3007 + month = internalGet(UCAL_MONTH); 1.3008 + } else { 1.3009 + month = getDefaultMonthInYear(year); 1.3010 + } 1.3011 + 1.3012 + int32_t julianDay = handleComputeMonthStart(year, useMonth ? month : 0, useMonth); 1.3013 + 1.3014 + if (bestField == UCAL_DAY_OF_MONTH) { 1.3015 + 1.3016 + // give calendar subclass a chance to have a default 'first' dom 1.3017 + int32_t dayOfMonth; 1.3018 + if(isSet(UCAL_DAY_OF_MONTH)) { 1.3019 + dayOfMonth = internalGet(UCAL_DAY_OF_MONTH,1); 1.3020 + } else { 1.3021 + dayOfMonth = getDefaultDayInMonth(year, month); 1.3022 + } 1.3023 + return julianDay + dayOfMonth; 1.3024 + } 1.3025 + 1.3026 + if (bestField == UCAL_DAY_OF_YEAR) { 1.3027 + return julianDay + internalGet(UCAL_DAY_OF_YEAR); 1.3028 + } 1.3029 + 1.3030 + int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw 1.3031 + 1.3032 + // At this point julianDay is the 0-based day BEFORE the first day of 1.3033 + // January 1, year 1 of the given calendar. If julianDay == 0, it 1.3034 + // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian 1.3035 + // or Gregorian). (or it is before the month we are in, if useMonth is True) 1.3036 + 1.3037 + // At this point we need to process the WEEK_OF_MONTH or 1.3038 + // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH. 1.3039 + // First, perform initial shared computations. These locate the 1.3040 + // first week of the period. 1.3041 + 1.3042 + // Get the 0-based localized DOW of day one of the month or year. 1.3043 + // Valid range 0..6. 1.3044 + int32_t first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; 1.3045 + if (first < 0) { 1.3046 + first += 7; 1.3047 + } 1.3048 + 1.3049 + int32_t dowLocal = getLocalDOW(); 1.3050 + 1.3051 + // Find the first target DOW (dowLocal) in the month or year. 1.3052 + // Actually, it may be just before the first of the month or year. 1.3053 + // It will be an integer from -5..7. 1.3054 + int32_t date = 1 - first + dowLocal; 1.3055 + 1.3056 + if (bestField == UCAL_DAY_OF_WEEK_IN_MONTH) { 1.3057 + // Adjust the target DOW to be in the month or year. 1.3058 + if (date < 1) { 1.3059 + date += 7; 1.3060 + } 1.3061 + 1.3062 + // The only trickiness occurs if the day-of-week-in-month is 1.3063 + // negative. 1.3064 + int32_t dim = internalGet(UCAL_DAY_OF_WEEK_IN_MONTH, 1); 1.3065 + if (dim >= 0) { 1.3066 + date += 7*(dim - 1); 1.3067 + 1.3068 + } else { 1.3069 + // Move date to the last of this day-of-week in this month, 1.3070 + // then back up as needed. If dim==-1, we don't back up at 1.3071 + // all. If dim==-2, we back up once, etc. Don't back up 1.3072 + // past the first of the given day-of-week in this month. 1.3073 + // Note that we handle -2, -3, etc. correctly, even though 1.3074 + // values < -1 are technically disallowed. 1.3075 + int32_t m = internalGet(UCAL_MONTH, UCAL_JANUARY); 1.3076 + int32_t monthLength = handleGetMonthLength(year, m); 1.3077 + date += ((monthLength - date) / 7 + dim + 1) * 7; 1.3078 + } 1.3079 + } else { 1.3080 +#if defined (U_DEBUG_CAL) 1.3081 + fprintf(stderr, "%s:%d - bf= %s\n", __FILE__, __LINE__, fldName(bestField)); 1.3082 +#endif 1.3083 + 1.3084 + if(bestField == UCAL_WEEK_OF_YEAR) { // ------------------------------------- WOY ------------- 1.3085 + if(!isSet(UCAL_YEAR_WOY) || // YWOY not set at all or 1.3086 + ( (resolveFields(kYearPrecedence) != UCAL_YEAR_WOY) // YWOY doesn't have precedence 1.3087 + && (fStamp[UCAL_YEAR_WOY]!=kInternallySet) ) ) // (excluding where all fields are internally set - then YWOY is used) 1.3088 + { 1.3089 + // need to be sure to stay in 'real' year. 1.3090 + int32_t woy = internalGet(bestField); 1.3091 + 1.3092 + int32_t nextJulianDay = handleComputeMonthStart(year+1, 0, FALSE); // jd of day before jan 1 1.3093 + int32_t nextFirst = julianDayToDayOfWeek(nextJulianDay + 1) - firstDayOfWeek; 1.3094 + 1.3095 + if (nextFirst < 0) { // 0..6 ldow of Jan 1 1.3096 + nextFirst += 7; 1.3097 + } 1.3098 + 1.3099 + if(woy==1) { // FIRST WEEK --------------------------------- 1.3100 +#if defined (U_DEBUG_CAL) 1.3101 + fprintf(stderr, "%s:%d - woy=%d, yp=%d, nj(%d)=%d, nf=%d", __FILE__, __LINE__, 1.3102 + internalGet(bestField), resolveFields(kYearPrecedence), year+1, 1.3103 + nextJulianDay, nextFirst); 1.3104 + 1.3105 + fprintf(stderr, " next: %d DFW, min=%d \n", (7-nextFirst), getMinimalDaysInFirstWeek() ); 1.3106 +#endif 1.3107 + 1.3108 + // nextFirst is now the localized DOW of Jan 1 of y-woy+1 1.3109 + if((nextFirst > 0) && // Jan 1 starts on FDOW 1.3110 + (7-nextFirst) >= getMinimalDaysInFirstWeek()) // or enough days in the week 1.3111 + { 1.3112 + // Jan 1 of (yearWoy+1) is in yearWoy+1 - recalculate JD to next year 1.3113 +#if defined (U_DEBUG_CAL) 1.3114 + fprintf(stderr, "%s:%d - was going to move JD from %d to %d [d%d]\n", __FILE__, __LINE__, 1.3115 + julianDay, nextJulianDay, (nextJulianDay-julianDay)); 1.3116 +#endif 1.3117 + julianDay = nextJulianDay; 1.3118 + 1.3119 + // recalculate 'first' [0-based local dow of jan 1] 1.3120 + first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; 1.3121 + if (first < 0) { 1.3122 + first += 7; 1.3123 + } 1.3124 + // recalculate date. 1.3125 + date = 1 - first + dowLocal; 1.3126 + } 1.3127 + } else if(woy>=getLeastMaximum(bestField)) { 1.3128 + // could be in the last week- find out if this JD would overstep 1.3129 + int32_t testDate = date; 1.3130 + if ((7 - first) < getMinimalDaysInFirstWeek()) { 1.3131 + testDate += 7; 1.3132 + } 1.3133 + 1.3134 + // Now adjust for the week number. 1.3135 + testDate += 7 * (woy - 1); 1.3136 + 1.3137 +#if defined (U_DEBUG_CAL) 1.3138 + fprintf(stderr, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n", 1.3139 + __FILE__, __LINE__, year, year-1, testDate, julianDay+testDate, nextJulianDay); 1.3140 +#endif 1.3141 + if(julianDay+testDate > nextJulianDay) { // is it past Dec 31? (nextJulianDay is day BEFORE year+1's Jan 1) 1.3142 + // Fire up the calculating engines.. retry YWOY = (year-1) 1.3143 + julianDay = handleComputeMonthStart(year-1, 0, FALSE); // jd before Jan 1 of previous year 1.3144 + first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; // 0 based local dow of first week 1.3145 + 1.3146 + if(first < 0) { // 0..6 1.3147 + first += 7; 1.3148 + } 1.3149 + date = 1 - first + dowLocal; 1.3150 + 1.3151 +#if defined (U_DEBUG_CAL) 1.3152 + fprintf(stderr, "%s:%d - date now %d, jd%d, ywoy%d\n", 1.3153 + __FILE__, __LINE__, date, julianDay, year-1); 1.3154 +#endif 1.3155 + 1.3156 + 1.3157 + } /* correction needed */ 1.3158 + } /* leastmaximum */ 1.3159 + } /* resolvefields(year) != year_woy */ 1.3160 + } /* bestfield != week_of_year */ 1.3161 + 1.3162 + // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR) 1.3163 + // Adjust for minimal days in first week 1.3164 + if ((7 - first) < getMinimalDaysInFirstWeek()) { 1.3165 + date += 7; 1.3166 + } 1.3167 + 1.3168 + // Now adjust for the week number. 1.3169 + date += 7 * (internalGet(bestField) - 1); 1.3170 + } 1.3171 + 1.3172 + return julianDay + date; 1.3173 +} 1.3174 + 1.3175 +int32_t 1.3176 +Calendar::getDefaultMonthInYear(int32_t /*eyear*/) 1.3177 +{ 1.3178 + return 0; 1.3179 +} 1.3180 + 1.3181 +int32_t 1.3182 +Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/) 1.3183 +{ 1.3184 + return 1; 1.3185 +} 1.3186 + 1.3187 + 1.3188 +int32_t Calendar::getLocalDOW() 1.3189 +{ 1.3190 + // Get zero-based localized DOW, valid range 0..6. This is the DOW 1.3191 + // we are looking for. 1.3192 + int32_t dowLocal = 0; 1.3193 + switch (resolveFields(kDOWPrecedence)) { 1.3194 + case UCAL_DAY_OF_WEEK: 1.3195 + dowLocal = internalGet(UCAL_DAY_OF_WEEK) - fFirstDayOfWeek; 1.3196 + break; 1.3197 + case UCAL_DOW_LOCAL: 1.3198 + dowLocal = internalGet(UCAL_DOW_LOCAL) - 1; 1.3199 + break; 1.3200 + default: 1.3201 + break; 1.3202 + } 1.3203 + dowLocal = dowLocal % 7; 1.3204 + if (dowLocal < 0) { 1.3205 + dowLocal += 7; 1.3206 + } 1.3207 + return dowLocal; 1.3208 +} 1.3209 + 1.3210 +int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy) 1.3211 +{ 1.3212 + // We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine 1.3213 + // what year we fall in, so that other code can set it properly. 1.3214 + // (code borrowed from computeWeekFields and handleComputeJulianDay) 1.3215 + //return yearWoy; 1.3216 + 1.3217 + // First, we need a reliable DOW. 1.3218 + UCalendarDateFields bestField = resolveFields(kDatePrecedence); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields 1.3219 + 1.3220 + // Now, a local DOW 1.3221 + int32_t dowLocal = getLocalDOW(); // 0..6 1.3222 + int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw 1.3223 + int32_t jan1Start = handleComputeMonthStart(yearWoy, 0, FALSE); 1.3224 + int32_t nextJan1Start = handleComputeMonthStart(yearWoy+1, 0, FALSE); // next year's Jan1 start 1.3225 + 1.3226 + // At this point julianDay is the 0-based day BEFORE the first day of 1.3227 + // January 1, year 1 of the given calendar. If julianDay == 0, it 1.3228 + // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian 1.3229 + // or Gregorian). (or it is before the month we are in, if useMonth is True) 1.3230 + 1.3231 + // At this point we need to process the WEEK_OF_MONTH or 1.3232 + // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH. 1.3233 + // First, perform initial shared computations. These locate the 1.3234 + // first week of the period. 1.3235 + 1.3236 + // Get the 0-based localized DOW of day one of the month or year. 1.3237 + // Valid range 0..6. 1.3238 + int32_t first = julianDayToDayOfWeek(jan1Start + 1) - firstDayOfWeek; 1.3239 + if (first < 0) { 1.3240 + first += 7; 1.3241 + } 1.3242 + int32_t nextFirst = julianDayToDayOfWeek(nextJan1Start + 1) - firstDayOfWeek; 1.3243 + if (nextFirst < 0) { 1.3244 + nextFirst += 7; 1.3245 + } 1.3246 + 1.3247 + int32_t minDays = getMinimalDaysInFirstWeek(); 1.3248 + UBool jan1InPrevYear = FALSE; // January 1st in the year of WOY is the 1st week? (i.e. first week is < minimal ) 1.3249 + //UBool nextJan1InPrevYear = FALSE; // January 1st of Year of WOY + 1 is in the first week? 1.3250 + 1.3251 + if((7 - first) < minDays) { 1.3252 + jan1InPrevYear = TRUE; 1.3253 + } 1.3254 + 1.3255 + // if((7 - nextFirst) < minDays) { 1.3256 + // nextJan1InPrevYear = TRUE; 1.3257 + // } 1.3258 + 1.3259 + switch(bestField) { 1.3260 + case UCAL_WEEK_OF_YEAR: 1.3261 + if(woy == 1) { 1.3262 + if(jan1InPrevYear == TRUE) { 1.3263 + // the first week of January is in the previous year 1.3264 + // therefore WOY1 is always solidly within yearWoy 1.3265 + return yearWoy; 1.3266 + } else { 1.3267 + // First WOY is split between two years 1.3268 + if( dowLocal < first) { // we are prior to Jan 1 1.3269 + return yearWoy-1; // previous year 1.3270 + } else { 1.3271 + return yearWoy; // in this year 1.3272 + } 1.3273 + } 1.3274 + } else if(woy >= getLeastMaximum(bestField)) { 1.3275 + // we _might_ be in the last week.. 1.3276 + int32_t jd = // Calculate JD of our target day: 1.3277 + jan1Start + // JD of Jan 1 1.3278 + (7-first) + // days in the first week (Jan 1.. ) 1.3279 + (woy-1)*7 + // add the weeks of the year 1.3280 + dowLocal; // the local dow (0..6) of last week 1.3281 + if(jan1InPrevYear==FALSE) { 1.3282 + jd -= 7; // woy already includes Jan 1's week. 1.3283 + } 1.3284 + 1.3285 + if( (jd+1) >= nextJan1Start ) { 1.3286 + // we are in week 52 or 53 etc. - actual year is yearWoy+1 1.3287 + return yearWoy+1; 1.3288 + } else { 1.3289 + // still in yearWoy; 1.3290 + return yearWoy; 1.3291 + } 1.3292 + } else { 1.3293 + // we're not possibly in the last week -must be ywoy 1.3294 + return yearWoy; 1.3295 + } 1.3296 + 1.3297 + case UCAL_DATE: 1.3298 + if((internalGet(UCAL_MONTH)==0) && 1.3299 + (woy >= getLeastMaximum(UCAL_WEEK_OF_YEAR))) { 1.3300 + return yearWoy+1; // month 0, late woy = in the next year 1.3301 + } else if(woy==1) { 1.3302 + //if(nextJan1InPrevYear) { 1.3303 + if(internalGet(UCAL_MONTH)==0) { 1.3304 + return yearWoy; 1.3305 + } else { 1.3306 + return yearWoy-1; 1.3307 + } 1.3308 + //} 1.3309 + } 1.3310 + 1.3311 + //(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow */ ) { 1.3312 + //within 1st week and in this month.. 1.3313 + //return yearWoy+1; 1.3314 + return yearWoy; 1.3315 + 1.3316 + default: // assume the year is appropriate 1.3317 + return yearWoy; 1.3318 + } 1.3319 +} 1.3320 + 1.3321 +int32_t Calendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const 1.3322 +{ 1.3323 + return handleComputeMonthStart(extendedYear, month+1, TRUE) - 1.3324 + handleComputeMonthStart(extendedYear, month, TRUE); 1.3325 +} 1.3326 + 1.3327 +int32_t Calendar::handleGetYearLength(int32_t eyear) const { 1.3328 + return handleComputeMonthStart(eyear+1, 0, FALSE) - 1.3329 + handleComputeMonthStart(eyear, 0, FALSE); 1.3330 +} 1.3331 + 1.3332 +int32_t 1.3333 +Calendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const 1.3334 +{ 1.3335 + int32_t result; 1.3336 + switch (field) { 1.3337 + case UCAL_DATE: 1.3338 + { 1.3339 + if(U_FAILURE(status)) return 0; 1.3340 + Calendar *cal = clone(); 1.3341 + if(!cal) { status = U_MEMORY_ALLOCATION_ERROR; return 0; } 1.3342 + cal->setLenient(TRUE); 1.3343 + cal->prepareGetActual(field,FALSE,status); 1.3344 + result = handleGetMonthLength(cal->get(UCAL_EXTENDED_YEAR, status), cal->get(UCAL_MONTH, status)); 1.3345 + delete cal; 1.3346 + } 1.3347 + break; 1.3348 + 1.3349 + case UCAL_DAY_OF_YEAR: 1.3350 + { 1.3351 + if(U_FAILURE(status)) return 0; 1.3352 + Calendar *cal = clone(); 1.3353 + if(!cal) { status = U_MEMORY_ALLOCATION_ERROR; return 0; } 1.3354 + cal->setLenient(TRUE); 1.3355 + cal->prepareGetActual(field,FALSE,status); 1.3356 + result = handleGetYearLength(cal->get(UCAL_EXTENDED_YEAR, status)); 1.3357 + delete cal; 1.3358 + } 1.3359 + break; 1.3360 + 1.3361 + case UCAL_DAY_OF_WEEK: 1.3362 + case UCAL_AM_PM: 1.3363 + case UCAL_HOUR: 1.3364 + case UCAL_HOUR_OF_DAY: 1.3365 + case UCAL_MINUTE: 1.3366 + case UCAL_SECOND: 1.3367 + case UCAL_MILLISECOND: 1.3368 + case UCAL_ZONE_OFFSET: 1.3369 + case UCAL_DST_OFFSET: 1.3370 + case UCAL_DOW_LOCAL: 1.3371 + case UCAL_JULIAN_DAY: 1.3372 + case UCAL_MILLISECONDS_IN_DAY: 1.3373 + // These fields all have fixed minima/maxima 1.3374 + result = getMaximum(field); 1.3375 + break; 1.3376 + 1.3377 + default: 1.3378 + // For all other fields, do it the hard way.... 1.3379 + result = getActualHelper(field, getLeastMaximum(field), getMaximum(field),status); 1.3380 + break; 1.3381 + } 1.3382 + return result; 1.3383 +} 1.3384 + 1.3385 + 1.3386 +/** 1.3387 +* Prepare this calendar for computing the actual minimum or maximum. 1.3388 +* This method modifies this calendar's fields; it is called on a 1.3389 +* temporary calendar. 1.3390 +* 1.3391 +* <p>Rationale: The semantics of getActualXxx() is to return the 1.3392 +* maximum or minimum value that the given field can take, taking into 1.3393 +* account other relevant fields. In general these other fields are 1.3394 +* larger fields. For example, when computing the actual maximum 1.3395 +* DATE, the current value of DATE itself is ignored, 1.3396 +* as is the value of any field smaller. 1.3397 +* 1.3398 +* <p>The time fields all have fixed minima and maxima, so we don't 1.3399 +* need to worry about them. This also lets us set the 1.3400 +* MILLISECONDS_IN_DAY to zero to erase any effects the time fields 1.3401 +* might have when computing date fields. 1.3402 +* 1.3403 +* <p>DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and 1.3404 +* WEEK_OF_YEAR fields to ensure that they are computed correctly. 1.3405 +* @internal 1.3406 +*/ 1.3407 +void Calendar::prepareGetActual(UCalendarDateFields field, UBool isMinimum, UErrorCode &status) 1.3408 +{ 1.3409 + set(UCAL_MILLISECONDS_IN_DAY, 0); 1.3410 + 1.3411 + switch (field) { 1.3412 + case UCAL_YEAR: 1.3413 + case UCAL_EXTENDED_YEAR: 1.3414 + set(UCAL_DAY_OF_YEAR, getGreatestMinimum(UCAL_DAY_OF_YEAR)); 1.3415 + break; 1.3416 + 1.3417 + case UCAL_YEAR_WOY: 1.3418 + set(UCAL_WEEK_OF_YEAR, getGreatestMinimum(UCAL_WEEK_OF_YEAR)); 1.3419 + 1.3420 + case UCAL_MONTH: 1.3421 + set(UCAL_DATE, getGreatestMinimum(UCAL_DATE)); 1.3422 + break; 1.3423 + 1.3424 + case UCAL_DAY_OF_WEEK_IN_MONTH: 1.3425 + // For dowim, the maximum occurs for the DOW of the first of the 1.3426 + // month. 1.3427 + set(UCAL_DATE, 1); 1.3428 + set(UCAL_DAY_OF_WEEK, get(UCAL_DAY_OF_WEEK, status)); // Make this user set 1.3429 + break; 1.3430 + 1.3431 + case UCAL_WEEK_OF_MONTH: 1.3432 + case UCAL_WEEK_OF_YEAR: 1.3433 + // If we're counting weeks, set the day of the week to either the 1.3434 + // first or last localized DOW. We know the last week of a month 1.3435 + // or year will contain the first day of the week, and that the 1.3436 + // first week will contain the last DOW. 1.3437 + { 1.3438 + int32_t dow = fFirstDayOfWeek; 1.3439 + if (isMinimum) { 1.3440 + dow = (dow + 6) % 7; // set to last DOW 1.3441 + if (dow < UCAL_SUNDAY) { 1.3442 + dow += 7; 1.3443 + } 1.3444 + } 1.3445 +#if defined (U_DEBUG_CAL) 1.3446 + fprintf(stderr, "prepareGetActualHelper(WOM/WOY) - dow=%d\n", dow); 1.3447 +#endif 1.3448 + set(UCAL_DAY_OF_WEEK, dow); 1.3449 + } 1.3450 + break; 1.3451 + default: 1.3452 + break; 1.3453 + } 1.3454 + 1.3455 + // Do this last to give it the newest time stamp 1.3456 + set(field, getGreatestMinimum(field)); 1.3457 +} 1.3458 + 1.3459 +int32_t Calendar::getActualHelper(UCalendarDateFields field, int32_t startValue, int32_t endValue, UErrorCode &status) const 1.3460 +{ 1.3461 +#if defined (U_DEBUG_CAL) 1.3462 + fprintf(stderr, "getActualHelper(%d,%d .. %d, %s)\n", field, startValue, endValue, u_errorName(status)); 1.3463 +#endif 1.3464 + if (startValue == endValue) { 1.3465 + // if we know that the maximum value is always the same, just return it 1.3466 + return startValue; 1.3467 + } 1.3468 + 1.3469 + int32_t delta = (endValue > startValue) ? 1 : -1; 1.3470 + 1.3471 + // clone the calendar so we don't mess with the real one, and set it to 1.3472 + // accept anything for the field values 1.3473 + if(U_FAILURE(status)) return startValue; 1.3474 + Calendar *work = clone(); 1.3475 + if(!work) { status = U_MEMORY_ALLOCATION_ERROR; return startValue; } 1.3476 + 1.3477 + // need to resolve time here, otherwise, fields set for actual limit 1.3478 + // may cause conflict with fields previously set (but not yet resolved). 1.3479 + work->complete(status); 1.3480 + 1.3481 + work->setLenient(TRUE); 1.3482 + work->prepareGetActual(field, delta < 0, status); 1.3483 + 1.3484 + // now try each value from the start to the end one by one until 1.3485 + // we get a value that normalizes to another value. The last value that 1.3486 + // normalizes to itself is the actual maximum for the current date 1.3487 + work->set(field, startValue); 1.3488 + 1.3489 + // prepareGetActual sets the first day of week in the same week with 1.3490 + // the first day of a month. Unlike WEEK_OF_YEAR, week number for the 1.3491 + // week which contains days from both previous and current month is 1.3492 + // not unique. For example, last several days in the previous month 1.3493 + // is week 5, and the rest of week is week 1. 1.3494 + int32_t result = startValue; 1.3495 + if ((work->get(field, status) != startValue 1.3496 + && field != UCAL_WEEK_OF_MONTH && delta > 0 ) || U_FAILURE(status)) { 1.3497 +#if defined (U_DEBUG_CAL) 1.3498 + fprintf(stderr, "getActualHelper(fld %d) - got %d (not %d) - %s\n", field, work->get(field,status), startValue, u_errorName(status)); 1.3499 +#endif 1.3500 + } else { 1.3501 + do { 1.3502 + startValue += delta; 1.3503 + work->add(field, delta, status); 1.3504 + if (work->get(field, status) != startValue || U_FAILURE(status)) { 1.3505 +#if defined (U_DEBUG_CAL) 1.3506 + fprintf(stderr, "getActualHelper(fld %d) - got %d (not %d), BREAK - %s\n", field, work->get(field,status), startValue, u_errorName(status)); 1.3507 +#endif 1.3508 + break; 1.3509 + } 1.3510 + result = startValue; 1.3511 + } while (startValue != endValue); 1.3512 + } 1.3513 + delete work; 1.3514 +#if defined (U_DEBUG_CAL) 1.3515 + fprintf(stderr, "getActualHelper(%d) = %d\n", field, result); 1.3516 +#endif 1.3517 + return result; 1.3518 +} 1.3519 + 1.3520 + 1.3521 + 1.3522 + 1.3523 +// ------------------------------------- 1.3524 + 1.3525 +void 1.3526 +Calendar::setWeekData(const Locale& desiredLocale, const char *type, UErrorCode& status) 1.3527 +{ 1.3528 + 1.3529 + if (U_FAILURE(status)) return; 1.3530 + 1.3531 + fFirstDayOfWeek = UCAL_SUNDAY; 1.3532 + fMinimalDaysInFirstWeek = 1; 1.3533 + fWeekendOnset = UCAL_SATURDAY; 1.3534 + fWeekendOnsetMillis = 0; 1.3535 + fWeekendCease = UCAL_SUNDAY; 1.3536 + fWeekendCeaseMillis = 86400000; // 24*60*60*1000 1.3537 + 1.3538 + // Since week and weekend data is territory based instead of language based, 1.3539 + // we may need to tweak the locale that we are using to try to get the appropriate 1.3540 + // values, using the following logic: 1.3541 + // 1). If the locale has a language but no territory, use the territory as defined by 1.3542 + // the likely subtags. 1.3543 + // 2). If the locale has a script designation then we ignore it, 1.3544 + // then remove it ( i.e. "en_Latn_US" becomes "en_US" ) 1.3545 + 1.3546 + char minLocaleID[ULOC_FULLNAME_CAPACITY] = { 0 }; 1.3547 + UErrorCode myStatus = U_ZERO_ERROR; 1.3548 + 1.3549 + uloc_minimizeSubtags(desiredLocale.getName(),minLocaleID,ULOC_FULLNAME_CAPACITY,&myStatus); 1.3550 + Locale min = Locale::createFromName(minLocaleID); 1.3551 + Locale useLocale; 1.3552 + if ( uprv_strlen(desiredLocale.getCountry()) == 0 || 1.3553 + (uprv_strlen(desiredLocale.getScript()) > 0 && uprv_strlen(min.getScript()) == 0) ) { 1.3554 + char maxLocaleID[ULOC_FULLNAME_CAPACITY] = { 0 }; 1.3555 + myStatus = U_ZERO_ERROR; 1.3556 + uloc_addLikelySubtags(desiredLocale.getName(),maxLocaleID,ULOC_FULLNAME_CAPACITY,&myStatus); 1.3557 + Locale max = Locale::createFromName(maxLocaleID); 1.3558 + useLocale = Locale(max.getLanguage(),max.getCountry()); 1.3559 + } else { 1.3560 + useLocale = Locale(desiredLocale); 1.3561 + } 1.3562 + 1.3563 + /* The code here is somewhat of a hack, since week data and weekend data aren't really tied to 1.3564 + a specific calendar, they aren't truly locale data. But this is the only place where valid and 1.3565 + actual locale can be set, so we take a shot at it here by loading a representative resource 1.3566 + from the calendar data. The code used to use the dateTimeElements resource to get first day 1.3567 + of week data, but this was moved to supplemental data under ticket 7755. (JCE) */ 1.3568 + 1.3569 + CalendarData calData(useLocale,type,status); 1.3570 + UResourceBundle *monthNames = calData.getByKey(gMonthNames,status); 1.3571 + if (U_SUCCESS(status)) { 1.3572 + U_LOCALE_BASED(locBased,*this); 1.3573 + locBased.setLocaleIDs(ures_getLocaleByType(monthNames, ULOC_VALID_LOCALE, &status), 1.3574 + ures_getLocaleByType(monthNames, ULOC_ACTUAL_LOCALE, &status)); 1.3575 + } else { 1.3576 + status = U_USING_FALLBACK_WARNING; 1.3577 + return; 1.3578 + } 1.3579 + 1.3580 + 1.3581 + // Read week data values from supplementalData week data 1.3582 + UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", &status); 1.3583 + ures_getByKey(rb, "weekData", rb, &status); 1.3584 + UResourceBundle *weekData = ures_getByKey(rb, useLocale.getCountry(), NULL, &status); 1.3585 + if (status == U_MISSING_RESOURCE_ERROR && rb != NULL) { 1.3586 + status = U_ZERO_ERROR; 1.3587 + weekData = ures_getByKey(rb, "001", NULL, &status); 1.3588 + } 1.3589 + 1.3590 + if (U_FAILURE(status)) { 1.3591 +#if defined (U_DEBUG_CALDATA) 1.3592 + fprintf(stderr, " Failure loading weekData from supplemental = %s\n", u_errorName(status)); 1.3593 +#endif 1.3594 + status = U_USING_FALLBACK_WARNING; 1.3595 + } else { 1.3596 + int32_t arrLen; 1.3597 + const int32_t *weekDataArr = ures_getIntVector(weekData,&arrLen,&status); 1.3598 + if( U_SUCCESS(status) && arrLen == 6 1.3599 + && 1 <= weekDataArr[0] && weekDataArr[0] <= 7 1.3600 + && 1 <= weekDataArr[1] && weekDataArr[1] <= 7 1.3601 + && 1 <= weekDataArr[2] && weekDataArr[2] <= 7 1.3602 + && 1 <= weekDataArr[4] && weekDataArr[4] <= 7) { 1.3603 + fFirstDayOfWeek = (UCalendarDaysOfWeek)weekDataArr[0]; 1.3604 + fMinimalDaysInFirstWeek = (uint8_t)weekDataArr[1]; 1.3605 + fWeekendOnset = (UCalendarDaysOfWeek)weekDataArr[2]; 1.3606 + fWeekendOnsetMillis = weekDataArr[3]; 1.3607 + fWeekendCease = (UCalendarDaysOfWeek)weekDataArr[4]; 1.3608 + fWeekendCeaseMillis = weekDataArr[5]; 1.3609 + } else { 1.3610 + status = U_INVALID_FORMAT_ERROR; 1.3611 + } 1.3612 + } 1.3613 + ures_close(weekData); 1.3614 + ures_close(rb); 1.3615 +} 1.3616 + 1.3617 +/** 1.3618 +* Recompute the time and update the status fields isTimeSet 1.3619 +* and areFieldsSet. Callers should check isTimeSet and only 1.3620 +* call this method if isTimeSet is false. 1.3621 +*/ 1.3622 +void 1.3623 +Calendar::updateTime(UErrorCode& status) 1.3624 +{ 1.3625 + computeTime(status); 1.3626 + if(U_FAILURE(status)) 1.3627 + return; 1.3628 + 1.3629 + // If we are lenient, we need to recompute the fields to normalize 1.3630 + // the values. Also, if we haven't set all the fields yet (i.e., 1.3631 + // in a newly-created object), we need to fill in the fields. [LIU] 1.3632 + if (isLenient() || ! fAreAllFieldsSet) 1.3633 + fAreFieldsSet = FALSE; 1.3634 + 1.3635 + fIsTimeSet = TRUE; 1.3636 + fAreFieldsVirtuallySet = FALSE; 1.3637 +} 1.3638 + 1.3639 +Locale 1.3640 +Calendar::getLocale(ULocDataLocaleType type, UErrorCode& status) const { 1.3641 + U_LOCALE_BASED(locBased, *this); 1.3642 + return locBased.getLocale(type, status); 1.3643 +} 1.3644 + 1.3645 +const char * 1.3646 +Calendar::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const { 1.3647 + U_LOCALE_BASED(locBased, *this); 1.3648 + return locBased.getLocaleID(type, status); 1.3649 +} 1.3650 + 1.3651 +void 1.3652 +Calendar::recalculateStamp() { 1.3653 + int32_t index; 1.3654 + int32_t currentValue; 1.3655 + int32_t j, i; 1.3656 + 1.3657 + fNextStamp = 1; 1.3658 + 1.3659 + for (j = 0; j < UCAL_FIELD_COUNT; j++) { 1.3660 + currentValue = STAMP_MAX; 1.3661 + index = -1; 1.3662 + for (i = 0; i < UCAL_FIELD_COUNT; i++) { 1.3663 + if (fStamp[i] > fNextStamp && fStamp[i] < currentValue) { 1.3664 + currentValue = fStamp[i]; 1.3665 + index = i; 1.3666 + } 1.3667 + } 1.3668 + 1.3669 + if (index >= 0) { 1.3670 + fStamp[index] = ++fNextStamp; 1.3671 + } else { 1.3672 + break; 1.3673 + } 1.3674 + } 1.3675 + fNextStamp++; 1.3676 +} 1.3677 + 1.3678 +// Deprecated function. This doesn't need to be inline. 1.3679 +void 1.3680 +Calendar::internalSet(EDateFields field, int32_t value) 1.3681 +{ 1.3682 + internalSet((UCalendarDateFields) field, value); 1.3683 +} 1.3684 + 1.3685 +BasicTimeZone* 1.3686 +Calendar::getBasicTimeZone(void) const { 1.3687 + if (dynamic_cast<const OlsonTimeZone *>(fZone) != NULL 1.3688 + || dynamic_cast<const SimpleTimeZone *>(fZone) != NULL 1.3689 + || dynamic_cast<const RuleBasedTimeZone *>(fZone) != NULL 1.3690 + || dynamic_cast<const VTimeZone *>(fZone) != NULL) { 1.3691 + return (BasicTimeZone*)fZone; 1.3692 + } 1.3693 + return NULL; 1.3694 +} 1.3695 + 1.3696 +U_NAMESPACE_END 1.3697 + 1.3698 +#endif /* #if !UCONFIG_NO_FORMATTING */ 1.3699 + 1.3700 + 1.3701 +//eof 1.3702 +