intl/icu/source/i18n/calendar.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial