intl/icu/source/i18n/gregocal.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 GREGOCAL.CPP
michael@0 8 *
michael@0 9 * Modification History:
michael@0 10 *
michael@0 11 * Date Name Description
michael@0 12 * 02/05/97 clhuang Creation.
michael@0 13 * 03/28/97 aliu Made highly questionable fix to computeFields to
michael@0 14 * handle DST correctly.
michael@0 15 * 04/22/97 aliu Cleaned up code drastically. Added monthLength().
michael@0 16 * Finished unimplemented parts of computeTime() for
michael@0 17 * week-based date determination. Removed quetionable
michael@0 18 * fix and wrote correct fix for computeFields() and
michael@0 19 * daylight time handling. Rewrote inDaylightTime()
michael@0 20 * and computeFields() to handle sensitive Daylight to
michael@0 21 * Standard time transitions correctly.
michael@0 22 * 05/08/97 aliu Added code review changes. Fixed isLeapYear() to
michael@0 23 * not cutover.
michael@0 24 * 08/12/97 aliu Added equivalentTo. Misc other fixes. Updated
michael@0 25 * add() from Java source.
michael@0 26 * 07/28/98 stephen Sync up with JDK 1.2
michael@0 27 * 09/14/98 stephen Changed type of kOneDay, kOneWeek to double.
michael@0 28 * Fixed bug in roll()
michael@0 29 * 10/15/99 aliu Fixed j31, incorrect WEEK_OF_YEAR computation.
michael@0 30 * 10/15/99 aliu Fixed j32, cannot set date to Feb 29 2000 AD.
michael@0 31 * {JDK bug 4210209 4209272}
michael@0 32 * 11/15/99 weiv Added YEAR_WOY and DOW_LOCAL computation
michael@0 33 * to timeToFields method, updated kMinValues, kMaxValues & kLeastMaxValues
michael@0 34 * 12/09/99 aliu Fixed j81, calculation errors and roll bugs
michael@0 35 * in year of cutover.
michael@0 36 * 01/24/2000 aliu Revised computeJulianDay for YEAR YEAR_WOY WOY.
michael@0 37 ********************************************************************************
michael@0 38 */
michael@0 39
michael@0 40 #include "unicode/utypes.h"
michael@0 41 #include <float.h>
michael@0 42
michael@0 43 #if !UCONFIG_NO_FORMATTING
michael@0 44
michael@0 45 #include "unicode/gregocal.h"
michael@0 46 #include "gregoimp.h"
michael@0 47 #include "umutex.h"
michael@0 48 #include "uassert.h"
michael@0 49
michael@0 50 // *****************************************************************************
michael@0 51 // class GregorianCalendar
michael@0 52 // *****************************************************************************
michael@0 53
michael@0 54 /**
michael@0 55 * Note that the Julian date used here is not a true Julian date, since
michael@0 56 * it is measured from midnight, not noon. This value is the Julian
michael@0 57 * day number of January 1, 1970 (Gregorian calendar) at noon UTC. [LIU]
michael@0 58 */
michael@0 59
michael@0 60 static const int16_t kNumDays[]
michael@0 61 = {0,31,59,90,120,151,181,212,243,273,304,334}; // 0-based, for day-in-year
michael@0 62 static const int16_t kLeapNumDays[]
michael@0 63 = {0,31,60,91,121,152,182,213,244,274,305,335}; // 0-based, for day-in-year
michael@0 64 static const int8_t kMonthLength[]
michael@0 65 = {31,28,31,30,31,30,31,31,30,31,30,31}; // 0-based
michael@0 66 static const int8_t kLeapMonthLength[]
michael@0 67 = {31,29,31,30,31,30,31,31,30,31,30,31}; // 0-based
michael@0 68
michael@0 69 // setTimeInMillis() limits the Julian day range to +/-7F000000.
michael@0 70 // This would seem to limit the year range to:
michael@0 71 // ms=+183882168921600000 jd=7f000000 December 20, 5828963 AD
michael@0 72 // ms=-184303902528000000 jd=81000000 September 20, 5838270 BC
michael@0 73 // HOWEVER, CalendarRegressionTest/Test4167060 shows that the actual
michael@0 74 // range limit on the year field is smaller (~ +/-140000). [alan 3.0]
michael@0 75
michael@0 76 static const int32_t kGregorianCalendarLimits[UCAL_FIELD_COUNT][4] = {
michael@0 77 // Minimum Greatest Least Maximum
michael@0 78 // Minimum Maximum
michael@0 79 { 0, 0, 1, 1}, // ERA
michael@0 80 { 1, 1, 140742, 144683}, // YEAR
michael@0 81 { 0, 0, 11, 11}, // MONTH
michael@0 82 { 1, 1, 52, 53}, // WEEK_OF_YEAR
michael@0 83 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
michael@0 84 { 1, 1, 28, 31}, // DAY_OF_MONTH
michael@0 85 { 1, 1, 365, 366}, // DAY_OF_YEAR
michael@0 86 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
michael@0 87 { -1, -1, 4, 5}, // DAY_OF_WEEK_IN_MONTH
michael@0 88 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
michael@0 89 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
michael@0 90 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
michael@0 91 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
michael@0 92 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
michael@0 93 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
michael@0 94 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
michael@0 95 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
michael@0 96 { -140742, -140742, 140742, 144683}, // YEAR_WOY
michael@0 97 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
michael@0 98 { -140742, -140742, 140742, 144683}, // EXTENDED_YEAR
michael@0 99 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
michael@0 100 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
michael@0 101 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
michael@0 102 };
michael@0 103
michael@0 104 /*
michael@0 105 * <pre>
michael@0 106 * Greatest Least
michael@0 107 * Field name Minimum Minimum Maximum Maximum
michael@0 108 * ---------- ------- ------- ------- -------
michael@0 109 * ERA 0 0 1 1
michael@0 110 * YEAR 1 1 140742 144683
michael@0 111 * MONTH 0 0 11 11
michael@0 112 * WEEK_OF_YEAR 1 1 52 53
michael@0 113 * WEEK_OF_MONTH 0 0 4 6
michael@0 114 * DAY_OF_MONTH 1 1 28 31
michael@0 115 * DAY_OF_YEAR 1 1 365 366
michael@0 116 * DAY_OF_WEEK 1 1 7 7
michael@0 117 * DAY_OF_WEEK_IN_MONTH -1 -1 4 5
michael@0 118 * AM_PM 0 0 1 1
michael@0 119 * HOUR 0 0 11 11
michael@0 120 * HOUR_OF_DAY 0 0 23 23
michael@0 121 * MINUTE 0 0 59 59
michael@0 122 * SECOND 0 0 59 59
michael@0 123 * MILLISECOND 0 0 999 999
michael@0 124 * ZONE_OFFSET -12* -12* 12* 12*
michael@0 125 * DST_OFFSET 0 0 1* 1*
michael@0 126 * YEAR_WOY 1 1 140742 144683
michael@0 127 * DOW_LOCAL 1 1 7 7
michael@0 128 * </pre>
michael@0 129 * (*) In units of one-hour
michael@0 130 */
michael@0 131
michael@0 132 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
michael@0 133 #include <stdio.h>
michael@0 134 #endif
michael@0 135
michael@0 136 U_NAMESPACE_BEGIN
michael@0 137
michael@0 138 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(GregorianCalendar)
michael@0 139
michael@0 140 // 00:00:00 UTC, October 15, 1582, expressed in ms from the epoch.
michael@0 141 // Note that only Italy and other Catholic countries actually
michael@0 142 // observed this cutover. Most other countries followed in
michael@0 143 // the next few centuries, some as late as 1928. [LIU]
michael@0 144 // in Java, -12219292800000L
michael@0 145 //const UDate GregorianCalendar::kPapalCutover = -12219292800000L;
michael@0 146 static const uint32_t kCutoverJulianDay = 2299161;
michael@0 147 static const UDate kPapalCutover = (2299161.0 - kEpochStartAsJulianDay) * U_MILLIS_PER_DAY;
michael@0 148 //static const UDate kPapalCutoverJulian = (2299161.0 - kEpochStartAsJulianDay);
michael@0 149
michael@0 150 // -------------------------------------
michael@0 151
michael@0 152 GregorianCalendar::GregorianCalendar(UErrorCode& status)
michael@0 153 : Calendar(status),
michael@0 154 fGregorianCutover(kPapalCutover),
michael@0 155 fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
michael@0 156 fIsGregorian(TRUE), fInvertGregorian(FALSE)
michael@0 157 {
michael@0 158 setTimeInMillis(getNow(), status);
michael@0 159 }
michael@0 160
michael@0 161 // -------------------------------------
michael@0 162
michael@0 163 GregorianCalendar::GregorianCalendar(TimeZone* zone, UErrorCode& status)
michael@0 164 : Calendar(zone, Locale::getDefault(), status),
michael@0 165 fGregorianCutover(kPapalCutover),
michael@0 166 fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
michael@0 167 fIsGregorian(TRUE), fInvertGregorian(FALSE)
michael@0 168 {
michael@0 169 setTimeInMillis(getNow(), status);
michael@0 170 }
michael@0 171
michael@0 172 // -------------------------------------
michael@0 173
michael@0 174 GregorianCalendar::GregorianCalendar(const TimeZone& zone, UErrorCode& status)
michael@0 175 : Calendar(zone, Locale::getDefault(), status),
michael@0 176 fGregorianCutover(kPapalCutover),
michael@0 177 fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
michael@0 178 fIsGregorian(TRUE), fInvertGregorian(FALSE)
michael@0 179 {
michael@0 180 setTimeInMillis(getNow(), status);
michael@0 181 }
michael@0 182
michael@0 183 // -------------------------------------
michael@0 184
michael@0 185 GregorianCalendar::GregorianCalendar(const Locale& aLocale, UErrorCode& status)
michael@0 186 : Calendar(TimeZone::createDefault(), aLocale, status),
michael@0 187 fGregorianCutover(kPapalCutover),
michael@0 188 fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
michael@0 189 fIsGregorian(TRUE), fInvertGregorian(FALSE)
michael@0 190 {
michael@0 191 setTimeInMillis(getNow(), status);
michael@0 192 }
michael@0 193
michael@0 194 // -------------------------------------
michael@0 195
michael@0 196 GregorianCalendar::GregorianCalendar(TimeZone* zone, const Locale& aLocale,
michael@0 197 UErrorCode& status)
michael@0 198 : Calendar(zone, aLocale, status),
michael@0 199 fGregorianCutover(kPapalCutover),
michael@0 200 fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
michael@0 201 fIsGregorian(TRUE), fInvertGregorian(FALSE)
michael@0 202 {
michael@0 203 setTimeInMillis(getNow(), status);
michael@0 204 }
michael@0 205
michael@0 206 // -------------------------------------
michael@0 207
michael@0 208 GregorianCalendar::GregorianCalendar(const TimeZone& zone, const Locale& aLocale,
michael@0 209 UErrorCode& status)
michael@0 210 : Calendar(zone, aLocale, status),
michael@0 211 fGregorianCutover(kPapalCutover),
michael@0 212 fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
michael@0 213 fIsGregorian(TRUE), fInvertGregorian(FALSE)
michael@0 214 {
michael@0 215 setTimeInMillis(getNow(), status);
michael@0 216 }
michael@0 217
michael@0 218 // -------------------------------------
michael@0 219
michael@0 220 GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date,
michael@0 221 UErrorCode& status)
michael@0 222 : Calendar(TimeZone::createDefault(), Locale::getDefault(), status),
michael@0 223 fGregorianCutover(kPapalCutover),
michael@0 224 fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
michael@0 225 fIsGregorian(TRUE), fInvertGregorian(FALSE)
michael@0 226 {
michael@0 227 set(UCAL_ERA, AD);
michael@0 228 set(UCAL_YEAR, year);
michael@0 229 set(UCAL_MONTH, month);
michael@0 230 set(UCAL_DATE, date);
michael@0 231 }
michael@0 232
michael@0 233 // -------------------------------------
michael@0 234
michael@0 235 GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date,
michael@0 236 int32_t hour, int32_t minute, UErrorCode& status)
michael@0 237 : Calendar(TimeZone::createDefault(), Locale::getDefault(), status),
michael@0 238 fGregorianCutover(kPapalCutover),
michael@0 239 fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
michael@0 240 fIsGregorian(TRUE), fInvertGregorian(FALSE)
michael@0 241 {
michael@0 242 set(UCAL_ERA, AD);
michael@0 243 set(UCAL_YEAR, year);
michael@0 244 set(UCAL_MONTH, month);
michael@0 245 set(UCAL_DATE, date);
michael@0 246 set(UCAL_HOUR_OF_DAY, hour);
michael@0 247 set(UCAL_MINUTE, minute);
michael@0 248 }
michael@0 249
michael@0 250 // -------------------------------------
michael@0 251
michael@0 252 GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date,
michael@0 253 int32_t hour, int32_t minute, int32_t second,
michael@0 254 UErrorCode& status)
michael@0 255 : Calendar(TimeZone::createDefault(), Locale::getDefault(), status),
michael@0 256 fGregorianCutover(kPapalCutover),
michael@0 257 fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582),
michael@0 258 fIsGregorian(TRUE), fInvertGregorian(FALSE)
michael@0 259 {
michael@0 260 set(UCAL_ERA, AD);
michael@0 261 set(UCAL_YEAR, year);
michael@0 262 set(UCAL_MONTH, month);
michael@0 263 set(UCAL_DATE, date);
michael@0 264 set(UCAL_HOUR_OF_DAY, hour);
michael@0 265 set(UCAL_MINUTE, minute);
michael@0 266 set(UCAL_SECOND, second);
michael@0 267 }
michael@0 268
michael@0 269 // -------------------------------------
michael@0 270
michael@0 271 GregorianCalendar::~GregorianCalendar()
michael@0 272 {
michael@0 273 }
michael@0 274
michael@0 275 // -------------------------------------
michael@0 276
michael@0 277 GregorianCalendar::GregorianCalendar(const GregorianCalendar &source)
michael@0 278 : Calendar(source),
michael@0 279 fGregorianCutover(source.fGregorianCutover),
michael@0 280 fCutoverJulianDay(source.fCutoverJulianDay), fNormalizedGregorianCutover(source.fNormalizedGregorianCutover), fGregorianCutoverYear(source.fGregorianCutoverYear),
michael@0 281 fIsGregorian(source.fIsGregorian), fInvertGregorian(source.fInvertGregorian)
michael@0 282 {
michael@0 283 }
michael@0 284
michael@0 285 // -------------------------------------
michael@0 286
michael@0 287 Calendar* GregorianCalendar::clone() const
michael@0 288 {
michael@0 289 return new GregorianCalendar(*this);
michael@0 290 }
michael@0 291
michael@0 292 // -------------------------------------
michael@0 293
michael@0 294 GregorianCalendar &
michael@0 295 GregorianCalendar::operator=(const GregorianCalendar &right)
michael@0 296 {
michael@0 297 if (this != &right)
michael@0 298 {
michael@0 299 Calendar::operator=(right);
michael@0 300 fGregorianCutover = right.fGregorianCutover;
michael@0 301 fNormalizedGregorianCutover = right.fNormalizedGregorianCutover;
michael@0 302 fGregorianCutoverYear = right.fGregorianCutoverYear;
michael@0 303 fCutoverJulianDay = right.fCutoverJulianDay;
michael@0 304 }
michael@0 305 return *this;
michael@0 306 }
michael@0 307
michael@0 308 // -------------------------------------
michael@0 309
michael@0 310 UBool GregorianCalendar::isEquivalentTo(const Calendar& other) const
michael@0 311 {
michael@0 312 // Calendar override.
michael@0 313 return Calendar::isEquivalentTo(other) &&
michael@0 314 fGregorianCutover == ((GregorianCalendar*)&other)->fGregorianCutover;
michael@0 315 }
michael@0 316
michael@0 317 // -------------------------------------
michael@0 318
michael@0 319 void
michael@0 320 GregorianCalendar::setGregorianChange(UDate date, UErrorCode& status)
michael@0 321 {
michael@0 322 if (U_FAILURE(status))
michael@0 323 return;
michael@0 324
michael@0 325 fGregorianCutover = date;
michael@0 326
michael@0 327 // Precompute two internal variables which we use to do the actual
michael@0 328 // cutover computations. These are the normalized cutover, which is the
michael@0 329 // midnight at or before the cutover, and the cutover year. The
michael@0 330 // normalized cutover is in pure date milliseconds; it contains no time
michael@0 331 // of day or timezone component, and it used to compare against other
michael@0 332 // pure date values.
michael@0 333 int32_t cutoverDay = (int32_t)ClockMath::floorDivide(fGregorianCutover, (double)kOneDay);
michael@0 334 fNormalizedGregorianCutover = cutoverDay * kOneDay;
michael@0 335
michael@0 336 // Handle the rare case of numeric overflow. If the user specifies a
michael@0 337 // change of UDate(Long.MIN_VALUE), in order to get a pure Gregorian
michael@0 338 // calendar, then the epoch day is -106751991168, which when multiplied
michael@0 339 // by ONE_DAY gives 9223372036794351616 -- the negative value is too
michael@0 340 // large for 64 bits, and overflows into a positive value. We correct
michael@0 341 // this by using the next day, which for all intents is semantically
michael@0 342 // equivalent.
michael@0 343 if (cutoverDay < 0 && fNormalizedGregorianCutover > 0) {
michael@0 344 fNormalizedGregorianCutover = (cutoverDay + 1) * kOneDay;
michael@0 345 }
michael@0 346
michael@0 347 // Normalize the year so BC values are represented as 0 and negative
michael@0 348 // values.
michael@0 349 GregorianCalendar *cal = new GregorianCalendar(getTimeZone(), status);
michael@0 350 /* test for NULL */
michael@0 351 if (cal == 0) {
michael@0 352 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 353 return;
michael@0 354 }
michael@0 355 if(U_FAILURE(status))
michael@0 356 return;
michael@0 357 cal->setTime(date, status);
michael@0 358 fGregorianCutoverYear = cal->get(UCAL_YEAR, status);
michael@0 359 if (cal->get(UCAL_ERA, status) == BC)
michael@0 360 fGregorianCutoverYear = 1 - fGregorianCutoverYear;
michael@0 361 fCutoverJulianDay = cutoverDay;
michael@0 362 delete cal;
michael@0 363 }
michael@0 364
michael@0 365
michael@0 366 void GregorianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) {
michael@0 367 int32_t eyear, month, dayOfMonth, dayOfYear, unusedRemainder;
michael@0 368
michael@0 369
michael@0 370 if(U_FAILURE(status)) {
michael@0 371 return;
michael@0 372 }
michael@0 373
michael@0 374 #if defined (U_DEBUG_CAL)
michael@0 375 fprintf(stderr, "%s:%d: jd%d- (greg's %d)- [cut=%d]\n",
michael@0 376 __FILE__, __LINE__, julianDay, getGregorianDayOfYear(), fCutoverJulianDay);
michael@0 377 #endif
michael@0 378
michael@0 379
michael@0 380 if (julianDay >= fCutoverJulianDay) {
michael@0 381 month = getGregorianMonth();
michael@0 382 dayOfMonth = getGregorianDayOfMonth();
michael@0 383 dayOfYear = getGregorianDayOfYear();
michael@0 384 eyear = getGregorianYear();
michael@0 385 } else {
michael@0 386 // The Julian epoch day (not the same as Julian Day)
michael@0 387 // is zero on Saturday December 30, 0 (Gregorian).
michael@0 388 int32_t julianEpochDay = julianDay - (kJan1_1JulianDay - 2);
michael@0 389 eyear = (int32_t) ClockMath::floorDivide((4.0*julianEpochDay) + 1464.0, (int32_t) 1461, unusedRemainder);
michael@0 390
michael@0 391 // Compute the Julian calendar day number for January 1, eyear
michael@0 392 int32_t january1 = 365*(eyear-1) + ClockMath::floorDivide(eyear-1, (int32_t)4);
michael@0 393 dayOfYear = (julianEpochDay - january1); // 0-based
michael@0 394
michael@0 395 // Julian leap years occurred historically every 4 years starting
michael@0 396 // with 8 AD. Before 8 AD the spacing is irregular; every 3 years
michael@0 397 // from 45 BC to 9 BC, and then none until 8 AD. However, we don't
michael@0 398 // implement this historical detail; instead, we implement the
michael@0 399 // computatinally cleaner proleptic calendar, which assumes
michael@0 400 // consistent 4-year cycles throughout time.
michael@0 401 UBool isLeap = ((eyear&0x3) == 0); // equiv. to (eyear%4 == 0)
michael@0 402
michael@0 403 // Common Julian/Gregorian calculation
michael@0 404 int32_t correction = 0;
michael@0 405 int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
michael@0 406 if (dayOfYear >= march1) {
michael@0 407 correction = isLeap ? 1 : 2;
michael@0 408 }
michael@0 409 month = (12 * (dayOfYear + correction) + 6) / 367; // zero-based month
michael@0 410 dayOfMonth = dayOfYear - (isLeap?kLeapNumDays[month]:kNumDays[month]) + 1; // one-based DOM
michael@0 411 ++dayOfYear;
michael@0 412 #if defined (U_DEBUG_CAL)
michael@0 413 // fprintf(stderr, "%d - %d[%d] + 1\n", dayOfYear, isLeap?kLeapNumDays[month]:kNumDays[month], month );
michael@0 414 // fprintf(stderr, "%s:%d: greg's HCF %d -> %d/%d/%d not %d/%d/%d\n",
michael@0 415 // __FILE__, __LINE__,julianDay,
michael@0 416 // eyear,month,dayOfMonth,
michael@0 417 // getGregorianYear(), getGregorianMonth(), getGregorianDayOfMonth() );
michael@0 418 fprintf(stderr, "%s:%d: doy %d (greg's %d)- [cut=%d]\n",
michael@0 419 __FILE__, __LINE__, dayOfYear, getGregorianDayOfYear(), fCutoverJulianDay);
michael@0 420 #endif
michael@0 421
michael@0 422 }
michael@0 423
michael@0 424 // [j81] if we are after the cutover in its year, shift the day of the year
michael@0 425 if((eyear == fGregorianCutoverYear) && (julianDay >= fCutoverJulianDay)) {
michael@0 426 //from handleComputeMonthStart
michael@0 427 int32_t gregShift = Grego::gregorianShift(eyear);
michael@0 428 #if defined (U_DEBUG_CAL)
michael@0 429 fprintf(stderr, "%s:%d: gregorian shift %d ::: doy%d => %d [cut=%d]\n",
michael@0 430 __FILE__, __LINE__,gregShift, dayOfYear, dayOfYear+gregShift, fCutoverJulianDay);
michael@0 431 #endif
michael@0 432 dayOfYear += gregShift;
michael@0 433 }
michael@0 434
michael@0 435 internalSet(UCAL_MONTH, month);
michael@0 436 internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
michael@0 437 internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
michael@0 438 internalSet(UCAL_EXTENDED_YEAR, eyear);
michael@0 439 int32_t era = AD;
michael@0 440 if (eyear < 1) {
michael@0 441 era = BC;
michael@0 442 eyear = 1 - eyear;
michael@0 443 }
michael@0 444 internalSet(UCAL_ERA, era);
michael@0 445 internalSet(UCAL_YEAR, eyear);
michael@0 446 }
michael@0 447
michael@0 448
michael@0 449 // -------------------------------------
michael@0 450
michael@0 451 UDate
michael@0 452 GregorianCalendar::getGregorianChange() const
michael@0 453 {
michael@0 454 return fGregorianCutover;
michael@0 455 }
michael@0 456
michael@0 457 // -------------------------------------
michael@0 458
michael@0 459 UBool
michael@0 460 GregorianCalendar::isLeapYear(int32_t year) const
michael@0 461 {
michael@0 462 // MSVC complains bitterly if we try to use Grego::isLeapYear here
michael@0 463 // NOTE: year&0x3 == year%4
michael@0 464 return (year >= fGregorianCutoverYear ?
michael@0 465 (((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0))) : // Gregorian
michael@0 466 ((year&0x3) == 0)); // Julian
michael@0 467 }
michael@0 468
michael@0 469 // -------------------------------------
michael@0 470
michael@0 471 int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField)
michael@0 472 {
michael@0 473 fInvertGregorian = FALSE;
michael@0 474
michael@0 475 int32_t jd = Calendar::handleComputeJulianDay(bestField);
michael@0 476
michael@0 477 if((bestField == UCAL_WEEK_OF_YEAR) && // if we are doing WOY calculations, we are counting relative to Jan 1 *julian*
michael@0 478 (internalGet(UCAL_EXTENDED_YEAR)==fGregorianCutoverYear) &&
michael@0 479 jd >= fCutoverJulianDay) {
michael@0 480 fInvertGregorian = TRUE; // So that the Julian Jan 1 will be used in handleComputeMonthStart
michael@0 481 return Calendar::handleComputeJulianDay(bestField);
michael@0 482 }
michael@0 483
michael@0 484
michael@0 485 // The following check handles portions of the cutover year BEFORE the
michael@0 486 // cutover itself happens.
michael@0 487 //if ((fIsGregorian==TRUE) != (jd >= fCutoverJulianDay)) { /* cutoverJulianDay)) { */
michael@0 488 if ((fIsGregorian==TRUE) != (jd >= fCutoverJulianDay)) { /* cutoverJulianDay)) { */
michael@0 489 #if defined (U_DEBUG_CAL)
michael@0 490 fprintf(stderr, "%s:%d: jd [invert] %d\n",
michael@0 491 __FILE__, __LINE__, jd);
michael@0 492 #endif
michael@0 493 fInvertGregorian = TRUE;
michael@0 494 jd = Calendar::handleComputeJulianDay(bestField);
michael@0 495 #if defined (U_DEBUG_CAL)
michael@0 496 fprintf(stderr, "%s:%d: fIsGregorian %s, fInvertGregorian %s - ",
michael@0 497 __FILE__, __LINE__,fIsGregorian?"T":"F", fInvertGregorian?"T":"F");
michael@0 498 fprintf(stderr, " jd NOW %d\n",
michael@0 499 jd);
michael@0 500 #endif
michael@0 501 } else {
michael@0 502 #if defined (U_DEBUG_CAL)
michael@0 503 fprintf(stderr, "%s:%d: jd [==] %d - %sfIsGregorian %sfInvertGregorian, %d\n",
michael@0 504 __FILE__, __LINE__, jd, fIsGregorian?"T":"F", fInvertGregorian?"T":"F", bestField);
michael@0 505 #endif
michael@0 506 }
michael@0 507
michael@0 508 if(fIsGregorian && (internalGet(UCAL_EXTENDED_YEAR) == fGregorianCutoverYear)) {
michael@0 509 int32_t gregShift = Grego::gregorianShift(internalGet(UCAL_EXTENDED_YEAR));
michael@0 510 if (bestField == UCAL_DAY_OF_YEAR) {
michael@0 511 #if defined (U_DEBUG_CAL)
michael@0 512 fprintf(stderr, "%s:%d: [DOY%d] gregorian shift of JD %d += %d\n",
michael@0 513 __FILE__, __LINE__, fFields[bestField],jd, gregShift);
michael@0 514 #endif
michael@0 515 jd -= gregShift;
michael@0 516 } else if ( bestField == UCAL_WEEK_OF_MONTH ) {
michael@0 517 int32_t weekShift = 14;
michael@0 518 #if defined (U_DEBUG_CAL)
michael@0 519 fprintf(stderr, "%s:%d: [WOY/WOM] gregorian week shift of %d += %d\n",
michael@0 520 __FILE__, __LINE__, jd, weekShift);
michael@0 521 #endif
michael@0 522 jd += weekShift; // shift by weeks for week based fields.
michael@0 523 }
michael@0 524 }
michael@0 525
michael@0 526 return jd;
michael@0 527 }
michael@0 528
michael@0 529 int32_t GregorianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month,
michael@0 530
michael@0 531 UBool /* useMonth */) const
michael@0 532 {
michael@0 533 GregorianCalendar *nonConstThis = (GregorianCalendar*)this; // cast away const
michael@0 534
michael@0 535 // If the month is out of range, adjust it into range, and
michael@0 536 // modify the extended year value accordingly.
michael@0 537 if (month < 0 || month > 11) {
michael@0 538 eyear += ClockMath::floorDivide(month, 12, month);
michael@0 539 }
michael@0 540
michael@0 541 UBool isLeap = eyear%4 == 0;
michael@0 542 int32_t y = eyear-1;
michael@0 543 int32_t julianDay = 365*y + ClockMath::floorDivide(y, 4) + (kJan1_1JulianDay - 3);
michael@0 544
michael@0 545 nonConstThis->fIsGregorian = (eyear >= fGregorianCutoverYear);
michael@0 546 #if defined (U_DEBUG_CAL)
michael@0 547 fprintf(stderr, "%s:%d: (hcms%d/%d) fIsGregorian %s, fInvertGregorian %s\n",
michael@0 548 __FILE__, __LINE__, eyear,month, fIsGregorian?"T":"F", fInvertGregorian?"T":"F");
michael@0 549 #endif
michael@0 550 if (fInvertGregorian) {
michael@0 551 nonConstThis->fIsGregorian = !fIsGregorian;
michael@0 552 }
michael@0 553 if (fIsGregorian) {
michael@0 554 isLeap = isLeap && ((eyear%100 != 0) || (eyear%400 == 0));
michael@0 555 // Add 2 because Gregorian calendar starts 2 days after
michael@0 556 // Julian calendar
michael@0 557 int32_t gregShift = Grego::gregorianShift(eyear);
michael@0 558 #if defined (U_DEBUG_CAL)
michael@0 559 fprintf(stderr, "%s:%d: (hcms%d/%d) gregorian shift of %d += %d\n",
michael@0 560 __FILE__, __LINE__, eyear, month, julianDay, gregShift);
michael@0 561 #endif
michael@0 562 julianDay += gregShift;
michael@0 563 }
michael@0 564
michael@0 565 // At this point julianDay indicates the day BEFORE the first
michael@0 566 // day of January 1, <eyear> of either the Julian or Gregorian
michael@0 567 // calendar.
michael@0 568
michael@0 569 if (month != 0) {
michael@0 570 julianDay += isLeap?kLeapNumDays[month]:kNumDays[month];
michael@0 571 }
michael@0 572
michael@0 573 return julianDay;
michael@0 574 }
michael@0 575
michael@0 576 int32_t GregorianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const
michael@0 577 {
michael@0 578 // If the month is out of range, adjust it into range, and
michael@0 579 // modify the extended year value accordingly.
michael@0 580 if (month < 0 || month > 11) {
michael@0 581 extendedYear += ClockMath::floorDivide(month, 12, month);
michael@0 582 }
michael@0 583
michael@0 584 return isLeapYear(extendedYear) ? kLeapMonthLength[month] : kMonthLength[month];
michael@0 585 }
michael@0 586
michael@0 587 int32_t GregorianCalendar::handleGetYearLength(int32_t eyear) const {
michael@0 588 return isLeapYear(eyear) ? 366 : 365;
michael@0 589 }
michael@0 590
michael@0 591
michael@0 592 int32_t
michael@0 593 GregorianCalendar::monthLength(int32_t month) const
michael@0 594 {
michael@0 595 int32_t year = internalGet(UCAL_EXTENDED_YEAR);
michael@0 596 return handleGetMonthLength(year, month);
michael@0 597 }
michael@0 598
michael@0 599 // -------------------------------------
michael@0 600
michael@0 601 int32_t
michael@0 602 GregorianCalendar::monthLength(int32_t month, int32_t year) const
michael@0 603 {
michael@0 604 return isLeapYear(year) ? kLeapMonthLength[month] : kMonthLength[month];
michael@0 605 }
michael@0 606
michael@0 607 // -------------------------------------
michael@0 608
michael@0 609 int32_t
michael@0 610 GregorianCalendar::yearLength(int32_t year) const
michael@0 611 {
michael@0 612 return isLeapYear(year) ? 366 : 365;
michael@0 613 }
michael@0 614
michael@0 615 // -------------------------------------
michael@0 616
michael@0 617 int32_t
michael@0 618 GregorianCalendar::yearLength() const
michael@0 619 {
michael@0 620 return isLeapYear(internalGet(UCAL_YEAR)) ? 366 : 365;
michael@0 621 }
michael@0 622
michael@0 623 // -------------------------------------
michael@0 624
michael@0 625 /**
michael@0 626 * After adjustments such as add(MONTH), add(YEAR), we don't want the
michael@0 627 * month to jump around. E.g., we don't want Jan 31 + 1 month to go to Mar
michael@0 628 * 3, we want it to go to Feb 28. Adjustments which might run into this
michael@0 629 * problem call this method to retain the proper month.
michael@0 630 */
michael@0 631 void
michael@0 632 GregorianCalendar::pinDayOfMonth()
michael@0 633 {
michael@0 634 int32_t monthLen = monthLength(internalGet(UCAL_MONTH));
michael@0 635 int32_t dom = internalGet(UCAL_DATE);
michael@0 636 if(dom > monthLen)
michael@0 637 set(UCAL_DATE, monthLen);
michael@0 638 }
michael@0 639
michael@0 640 // -------------------------------------
michael@0 641
michael@0 642
michael@0 643 UBool
michael@0 644 GregorianCalendar::validateFields() const
michael@0 645 {
michael@0 646 for (int32_t field = 0; field < UCAL_FIELD_COUNT; field++) {
michael@0 647 // Ignore DATE and DAY_OF_YEAR which are handled below
michael@0 648 if (field != UCAL_DATE &&
michael@0 649 field != UCAL_DAY_OF_YEAR &&
michael@0 650 isSet((UCalendarDateFields)field) &&
michael@0 651 ! boundsCheck(internalGet((UCalendarDateFields)field), (UCalendarDateFields)field))
michael@0 652 return FALSE;
michael@0 653 }
michael@0 654
michael@0 655 // Values differ in Least-Maximum and Maximum should be handled
michael@0 656 // specially.
michael@0 657 if (isSet(UCAL_DATE)) {
michael@0 658 int32_t date = internalGet(UCAL_DATE);
michael@0 659 if (date < getMinimum(UCAL_DATE) ||
michael@0 660 date > monthLength(internalGet(UCAL_MONTH))) {
michael@0 661 return FALSE;
michael@0 662 }
michael@0 663 }
michael@0 664
michael@0 665 if (isSet(UCAL_DAY_OF_YEAR)) {
michael@0 666 int32_t days = internalGet(UCAL_DAY_OF_YEAR);
michael@0 667 if (days < 1 || days > yearLength()) {
michael@0 668 return FALSE;
michael@0 669 }
michael@0 670 }
michael@0 671
michael@0 672 // Handle DAY_OF_WEEK_IN_MONTH, which must not have the value zero.
michael@0 673 // We've checked against minimum and maximum above already.
michael@0 674 if (isSet(UCAL_DAY_OF_WEEK_IN_MONTH) &&
michael@0 675 0 == internalGet(UCAL_DAY_OF_WEEK_IN_MONTH)) {
michael@0 676 return FALSE;
michael@0 677 }
michael@0 678
michael@0 679 return TRUE;
michael@0 680 }
michael@0 681
michael@0 682 // -------------------------------------
michael@0 683
michael@0 684 UBool
michael@0 685 GregorianCalendar::boundsCheck(int32_t value, UCalendarDateFields field) const
michael@0 686 {
michael@0 687 return value >= getMinimum(field) && value <= getMaximum(field);
michael@0 688 }
michael@0 689
michael@0 690 // -------------------------------------
michael@0 691
michael@0 692 UDate
michael@0 693 GregorianCalendar::getEpochDay(UErrorCode& status)
michael@0 694 {
michael@0 695 complete(status);
michael@0 696 // Divide by 1000 (convert to seconds) in order to prevent overflow when
michael@0 697 // dealing with UDate(Long.MIN_VALUE) and UDate(Long.MAX_VALUE).
michael@0 698 double wallSec = internalGetTime()/1000 + (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET))/1000;
michael@0 699
michael@0 700 return ClockMath::floorDivide(wallSec, kOneDay/1000.0);
michael@0 701 }
michael@0 702
michael@0 703 // -------------------------------------
michael@0 704
michael@0 705
michael@0 706 // -------------------------------------
michael@0 707
michael@0 708 /**
michael@0 709 * Compute the julian day number of the day BEFORE the first day of
michael@0 710 * January 1, year 1 of the given calendar. If julianDay == 0, it
michael@0 711 * specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
michael@0 712 * or Gregorian).
michael@0 713 */
michael@0 714 double GregorianCalendar::computeJulianDayOfYear(UBool isGregorian,
michael@0 715 int32_t year, UBool& isLeap)
michael@0 716 {
michael@0 717 isLeap = year%4 == 0;
michael@0 718 int32_t y = year - 1;
michael@0 719 double julianDay = 365.0*y + ClockMath::floorDivide(y, 4) + (kJan1_1JulianDay - 3);
michael@0 720
michael@0 721 if (isGregorian) {
michael@0 722 isLeap = isLeap && ((year%100 != 0) || (year%400 == 0));
michael@0 723 // Add 2 because Gregorian calendar starts 2 days after Julian calendar
michael@0 724 julianDay += Grego::gregorianShift(year);
michael@0 725 }
michael@0 726
michael@0 727 return julianDay;
michael@0 728 }
michael@0 729
michael@0 730 // /**
michael@0 731 // * Compute the day of week, relative to the first day of week, from
michael@0 732 // * 0..6, of the current DOW_LOCAL or DAY_OF_WEEK fields. This is
michael@0 733 // * equivalent to get(DOW_LOCAL) - 1.
michael@0 734 // */
michael@0 735 // int32_t GregorianCalendar::computeRelativeDOW() const {
michael@0 736 // int32_t relDow = 0;
michael@0 737 // if (fStamp[UCAL_DOW_LOCAL] > fStamp[UCAL_DAY_OF_WEEK]) {
michael@0 738 // relDow = internalGet(UCAL_DOW_LOCAL) - 1; // 1-based
michael@0 739 // } else if (fStamp[UCAL_DAY_OF_WEEK] != kUnset) {
michael@0 740 // relDow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
michael@0 741 // if (relDow < 0) relDow += 7;
michael@0 742 // }
michael@0 743 // return relDow;
michael@0 744 // }
michael@0 745
michael@0 746 // /**
michael@0 747 // * Compute the day of week, relative to the first day of week,
michael@0 748 // * from 0..6 of the given julian day.
michael@0 749 // */
michael@0 750 // int32_t GregorianCalendar::computeRelativeDOW(double julianDay) const {
michael@0 751 // int32_t relDow = julianDayToDayOfWeek(julianDay) - getFirstDayOfWeek();
michael@0 752 // if (relDow < 0) {
michael@0 753 // relDow += 7;
michael@0 754 // }
michael@0 755 // return relDow;
michael@0 756 // }
michael@0 757
michael@0 758 // /**
michael@0 759 // * Compute the DOY using the WEEK_OF_YEAR field and the julian day
michael@0 760 // * of the day BEFORE January 1 of a year (a return value from
michael@0 761 // * computeJulianDayOfYear).
michael@0 762 // */
michael@0 763 // int32_t GregorianCalendar::computeDOYfromWOY(double julianDayOfYear) const {
michael@0 764 // // Compute DOY from day of week plus week of year
michael@0 765
michael@0 766 // // Find the day of the week for the first of this year. This
michael@0 767 // // is zero-based, with 0 being the locale-specific first day of
michael@0 768 // // the week. Add 1 to get first day of year.
michael@0 769 // int32_t fdy = computeRelativeDOW(julianDayOfYear + 1);
michael@0 770
michael@0 771 // return
michael@0 772 // // Compute doy of first (relative) DOW of WOY 1
michael@0 773 // (((7 - fdy) < getMinimalDaysInFirstWeek())
michael@0 774 // ? (8 - fdy) : (1 - fdy))
michael@0 775
michael@0 776 // // Adjust for the week number.
michael@0 777 // + (7 * (internalGet(UCAL_WEEK_OF_YEAR) - 1))
michael@0 778
michael@0 779 // // Adjust for the DOW
michael@0 780 // + computeRelativeDOW();
michael@0 781 // }
michael@0 782
michael@0 783 // -------------------------------------
michael@0 784
michael@0 785 double
michael@0 786 GregorianCalendar::millisToJulianDay(UDate millis)
michael@0 787 {
michael@0 788 return (double)kEpochStartAsJulianDay + ClockMath::floorDivide(millis, (double)kOneDay);
michael@0 789 }
michael@0 790
michael@0 791 // -------------------------------------
michael@0 792
michael@0 793 UDate
michael@0 794 GregorianCalendar::julianDayToMillis(double julian)
michael@0 795 {
michael@0 796 return (UDate) ((julian - kEpochStartAsJulianDay) * (double) kOneDay);
michael@0 797 }
michael@0 798
michael@0 799 // -------------------------------------
michael@0 800
michael@0 801 int32_t
michael@0 802 GregorianCalendar::aggregateStamp(int32_t stamp_a, int32_t stamp_b)
michael@0 803 {
michael@0 804 return (((stamp_a != kUnset && stamp_b != kUnset)
michael@0 805 ? uprv_max(stamp_a, stamp_b)
michael@0 806 : (int32_t)kUnset));
michael@0 807 }
michael@0 808
michael@0 809 // -------------------------------------
michael@0 810
michael@0 811 /**
michael@0 812 * Roll a field by a signed amount.
michael@0 813 * Note: This will be made public later. [LIU]
michael@0 814 */
michael@0 815
michael@0 816 void
michael@0 817 GregorianCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) {
michael@0 818 roll((UCalendarDateFields) field, amount, status);
michael@0 819 }
michael@0 820
michael@0 821 void
michael@0 822 GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status)
michael@0 823 {
michael@0 824 if((amount == 0) || U_FAILURE(status)) {
michael@0 825 return;
michael@0 826 }
michael@0 827
michael@0 828 // J81 processing. (gregorian cutover)
michael@0 829 UBool inCutoverMonth = FALSE;
michael@0 830 int32_t cMonthLen=0; // 'c' for cutover; in days
michael@0 831 int32_t cDayOfMonth=0; // no discontinuity: [0, cMonthLen)
michael@0 832 double cMonthStart=0.0; // in ms
michael@0 833
michael@0 834 // Common code - see if we're in the cutover month of the cutover year
michael@0 835 if(get(UCAL_EXTENDED_YEAR, status) == fGregorianCutoverYear) {
michael@0 836 switch (field) {
michael@0 837 case UCAL_DAY_OF_MONTH:
michael@0 838 case UCAL_WEEK_OF_MONTH:
michael@0 839 {
michael@0 840 int32_t max = monthLength(internalGet(UCAL_MONTH));
michael@0 841 UDate t = internalGetTime();
michael@0 842 // We subtract 1 from the DAY_OF_MONTH to make it zero-based, and an
michael@0 843 // additional 10 if we are after the cutover. Thus the monthStart
michael@0 844 // value will be correct iff we actually are in the cutover month.
michael@0 845 cDayOfMonth = internalGet(UCAL_DAY_OF_MONTH) - ((t >= fGregorianCutover) ? 10 : 0);
michael@0 846 cMonthStart = t - ((cDayOfMonth - 1) * kOneDay);
michael@0 847 // A month containing the cutover is 10 days shorter.
michael@0 848 if ((cMonthStart < fGregorianCutover) &&
michael@0 849 (cMonthStart + (cMonthLen=(max-10))*kOneDay >= fGregorianCutover)) {
michael@0 850 inCutoverMonth = TRUE;
michael@0 851 }
michael@0 852 }
michael@0 853 default:
michael@0 854 ;
michael@0 855 }
michael@0 856 }
michael@0 857
michael@0 858 switch (field) {
michael@0 859 case UCAL_WEEK_OF_YEAR: {
michael@0 860 // Unlike WEEK_OF_MONTH, WEEK_OF_YEAR never shifts the day of the
michael@0 861 // week. Also, rolling the week of the year can have seemingly
michael@0 862 // strange effects simply because the year of the week of year
michael@0 863 // may be different from the calendar year. For example, the
michael@0 864 // date Dec 28, 1997 is the first day of week 1 of 1998 (if
michael@0 865 // weeks start on Sunday and the minimal days in first week is
michael@0 866 // <= 3).
michael@0 867 int32_t woy = get(UCAL_WEEK_OF_YEAR, status);
michael@0 868 // Get the ISO year, which matches the week of year. This
michael@0 869 // may be one year before or after the calendar year.
michael@0 870 int32_t isoYear = get(UCAL_YEAR_WOY, status);
michael@0 871 int32_t isoDoy = internalGet(UCAL_DAY_OF_YEAR);
michael@0 872 if (internalGet(UCAL_MONTH) == UCAL_JANUARY) {
michael@0 873 if (woy >= 52) {
michael@0 874 isoDoy += handleGetYearLength(isoYear);
michael@0 875 }
michael@0 876 } else {
michael@0 877 if (woy == 1) {
michael@0 878 isoDoy -= handleGetYearLength(isoYear - 1);
michael@0 879 }
michael@0 880 }
michael@0 881 woy += amount;
michael@0 882 // Do fast checks to avoid unnecessary computation:
michael@0 883 if (woy < 1 || woy > 52) {
michael@0 884 // Determine the last week of the ISO year.
michael@0 885 // We do this using the standard formula we use
michael@0 886 // everywhere in this file. If we can see that the
michael@0 887 // days at the end of the year are going to fall into
michael@0 888 // week 1 of the next year, we drop the last week by
michael@0 889 // subtracting 7 from the last day of the year.
michael@0 890 int32_t lastDoy = handleGetYearLength(isoYear);
michael@0 891 int32_t lastRelDow = (lastDoy - isoDoy + internalGet(UCAL_DAY_OF_WEEK) -
michael@0 892 getFirstDayOfWeek()) % 7;
michael@0 893 if (lastRelDow < 0) lastRelDow += 7;
michael@0 894 if ((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) lastDoy -= 7;
michael@0 895 int32_t lastWoy = weekNumber(lastDoy, lastRelDow + 1);
michael@0 896 woy = ((woy + lastWoy - 1) % lastWoy) + 1;
michael@0 897 }
michael@0 898 set(UCAL_WEEK_OF_YEAR, woy);
michael@0 899 set(UCAL_YEAR_WOY,isoYear);
michael@0 900 return;
michael@0 901 }
michael@0 902
michael@0 903 case UCAL_DAY_OF_MONTH:
michael@0 904 if( !inCutoverMonth ) {
michael@0 905 Calendar::roll(field, amount, status);
michael@0 906 return;
michael@0 907 } else {
michael@0 908 // [j81] 1582 special case for DOM
michael@0 909 // The default computation works except when the current month
michael@0 910 // contains the Gregorian cutover. We handle this special case
michael@0 911 // here. [j81 - aliu]
michael@0 912 double monthLen = cMonthLen * kOneDay;
michael@0 913 double msIntoMonth = uprv_fmod(internalGetTime() - cMonthStart +
michael@0 914 amount * kOneDay, monthLen);
michael@0 915 if (msIntoMonth < 0) {
michael@0 916 msIntoMonth += monthLen;
michael@0 917 }
michael@0 918 #if defined (U_DEBUG_CAL)
michael@0 919 fprintf(stderr, "%s:%d: roll DOM %d -> %.0lf ms \n",
michael@0 920 __FILE__, __LINE__,amount, cMonthLen, cMonthStart+msIntoMonth);
michael@0 921 #endif
michael@0 922 setTimeInMillis(cMonthStart + msIntoMonth, status);
michael@0 923 return;
michael@0 924 }
michael@0 925
michael@0 926 case UCAL_WEEK_OF_MONTH:
michael@0 927 if( !inCutoverMonth ) {
michael@0 928 Calendar::roll(field, amount, status);
michael@0 929 return;
michael@0 930 } else {
michael@0 931 #if defined (U_DEBUG_CAL)
michael@0 932 fprintf(stderr, "%s:%d: roll WOM %d ??????????????????? \n",
michael@0 933 __FILE__, __LINE__,amount);
michael@0 934 #endif
michael@0 935 // NOTE: following copied from the old
michael@0 936 // GregorianCalendar::roll( WEEK_OF_MONTH ) code
michael@0 937
michael@0 938 // This is tricky, because during the roll we may have to shift
michael@0 939 // to a different day of the week. For example:
michael@0 940
michael@0 941 // s m t w r f s
michael@0 942 // 1 2 3 4 5
michael@0 943 // 6 7 8 9 10 11 12
michael@0 944
michael@0 945 // When rolling from the 6th or 7th back one week, we go to the
michael@0 946 // 1st (assuming that the first partial week counts). The same
michael@0 947 // thing happens at the end of the month.
michael@0 948
michael@0 949 // The other tricky thing is that we have to figure out whether
michael@0 950 // the first partial week actually counts or not, based on the
michael@0 951 // minimal first days in the week. And we have to use the
michael@0 952 // correct first day of the week to delineate the week
michael@0 953 // boundaries.
michael@0 954
michael@0 955 // Here's our algorithm. First, we find the real boundaries of
michael@0 956 // the month. Then we discard the first partial week if it
michael@0 957 // doesn't count in this locale. Then we fill in the ends with
michael@0 958 // phantom days, so that the first partial week and the last
michael@0 959 // partial week are full weeks. We then have a nice square
michael@0 960 // block of weeks. We do the usual rolling within this block,
michael@0 961 // as is done elsewhere in this method. If we wind up on one of
michael@0 962 // the phantom days that we added, we recognize this and pin to
michael@0 963 // the first or the last day of the month. Easy, eh?
michael@0 964
michael@0 965 // Another wrinkle: To fix jitterbug 81, we have to make all this
michael@0 966 // work in the oddball month containing the Gregorian cutover.
michael@0 967 // This month is 10 days shorter than usual, and also contains
michael@0 968 // a discontinuity in the days; e.g., the default cutover month
michael@0 969 // is Oct 1582, and goes from day of month 4 to day of month 15.
michael@0 970
michael@0 971 // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
michael@0 972 // in this locale. We have dow in 0..6.
michael@0 973 int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
michael@0 974 if (dow < 0)
michael@0 975 dow += 7;
michael@0 976
michael@0 977 // Find the day of month, compensating for cutover discontinuity.
michael@0 978 int32_t dom = cDayOfMonth;
michael@0 979
michael@0 980 // Find the day of the week (normalized for locale) for the first
michael@0 981 // of the month.
michael@0 982 int32_t fdm = (dow - dom + 1) % 7;
michael@0 983 if (fdm < 0)
michael@0 984 fdm += 7;
michael@0 985
michael@0 986 // Get the first day of the first full week of the month,
michael@0 987 // including phantom days, if any. Figure out if the first week
michael@0 988 // counts or not; if it counts, then fill in phantom days. If
michael@0 989 // not, advance to the first real full week (skip the partial week).
michael@0 990 int32_t start;
michael@0 991 if ((7 - fdm) < getMinimalDaysInFirstWeek())
michael@0 992 start = 8 - fdm; // Skip the first partial week
michael@0 993 else
michael@0 994 start = 1 - fdm; // This may be zero or negative
michael@0 995
michael@0 996 // Get the day of the week (normalized for locale) for the last
michael@0 997 // day of the month.
michael@0 998 int32_t monthLen = cMonthLen;
michael@0 999 int32_t ldm = (monthLen - dom + dow) % 7;
michael@0 1000 // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.
michael@0 1001
michael@0 1002 // Get the limit day for the blocked-off rectangular month; that
michael@0 1003 // is, the day which is one past the last day of the month,
michael@0 1004 // after the month has already been filled in with phantom days
michael@0 1005 // to fill out the last week. This day has a normalized DOW of 0.
michael@0 1006 int32_t limit = monthLen + 7 - ldm;
michael@0 1007
michael@0 1008 // Now roll between start and (limit - 1).
michael@0 1009 int32_t gap = limit - start;
michael@0 1010 int32_t newDom = (dom + amount*7 - start) % gap;
michael@0 1011 if (newDom < 0)
michael@0 1012 newDom += gap;
michael@0 1013 newDom += start;
michael@0 1014
michael@0 1015 // Finally, pin to the real start and end of the month.
michael@0 1016 if (newDom < 1)
michael@0 1017 newDom = 1;
michael@0 1018 if (newDom > monthLen)
michael@0 1019 newDom = monthLen;
michael@0 1020
michael@0 1021 // Set the DAY_OF_MONTH. We rely on the fact that this field
michael@0 1022 // takes precedence over everything else (since all other fields
michael@0 1023 // are also set at this point). If this fact changes (if the
michael@0 1024 // disambiguation algorithm changes) then we will have to unset
michael@0 1025 // the appropriate fields here so that DAY_OF_MONTH is attended
michael@0 1026 // to.
michael@0 1027
michael@0 1028 // If we are in the cutover month, manipulate ms directly. Don't do
michael@0 1029 // this in general because it doesn't work across DST boundaries
michael@0 1030 // (details, details). This takes care of the discontinuity.
michael@0 1031 setTimeInMillis(cMonthStart + (newDom-1)*kOneDay, status);
michael@0 1032 return;
michael@0 1033 }
michael@0 1034
michael@0 1035 default:
michael@0 1036 Calendar::roll(field, amount, status);
michael@0 1037 return;
michael@0 1038 }
michael@0 1039 }
michael@0 1040
michael@0 1041 // -------------------------------------
michael@0 1042
michael@0 1043
michael@0 1044 /**
michael@0 1045 * Return the minimum value that this field could have, given the current date.
michael@0 1046 * For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum().
michael@0 1047 * @param field the time field.
michael@0 1048 * @return the minimum value that this field could have, given the current date.
michael@0 1049 * @deprecated ICU 2.6. Use getActualMinimum(UCalendarDateFields field) instead.
michael@0 1050 */
michael@0 1051 int32_t GregorianCalendar::getActualMinimum(EDateFields field) const
michael@0 1052 {
michael@0 1053 return getMinimum((UCalendarDateFields)field);
michael@0 1054 }
michael@0 1055
michael@0 1056 int32_t GregorianCalendar::getActualMinimum(EDateFields field, UErrorCode& /* status */) const
michael@0 1057 {
michael@0 1058 return getMinimum((UCalendarDateFields)field);
michael@0 1059 }
michael@0 1060
michael@0 1061 /**
michael@0 1062 * Return the minimum value that this field could have, given the current date.
michael@0 1063 * For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum().
michael@0 1064 * @param field the time field.
michael@0 1065 * @return the minimum value that this field could have, given the current date.
michael@0 1066 * @draft ICU 2.6.
michael@0 1067 */
michael@0 1068 int32_t GregorianCalendar::getActualMinimum(UCalendarDateFields field, UErrorCode& /* status */) const
michael@0 1069 {
michael@0 1070 return getMinimum(field);
michael@0 1071 }
michael@0 1072
michael@0 1073
michael@0 1074 // ------------------------------------
michael@0 1075
michael@0 1076 /**
michael@0 1077 * Old year limits were least max 292269054, max 292278994.
michael@0 1078 */
michael@0 1079
michael@0 1080 /**
michael@0 1081 * @stable ICU 2.0
michael@0 1082 */
michael@0 1083 int32_t GregorianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
michael@0 1084 return kGregorianCalendarLimits[field][limitType];
michael@0 1085 }
michael@0 1086
michael@0 1087 /**
michael@0 1088 * Return the maximum value that this field could have, given the current date.
michael@0 1089 * For example, with the date "Feb 3, 1997" and the DAY_OF_MONTH field, the actual
michael@0 1090 * maximum would be 28; for "Feb 3, 1996" it s 29. Similarly for a Hebrew calendar,
michael@0 1091 * for some years the actual maximum for MONTH is 12, and for others 13.
michael@0 1092 * @stable ICU 2.0
michael@0 1093 */
michael@0 1094 int32_t GregorianCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const
michael@0 1095 {
michael@0 1096 /* It is a known limitation that the code here (and in getActualMinimum)
michael@0 1097 * won't behave properly at the extreme limits of GregorianCalendar's
michael@0 1098 * representable range (except for the code that handles the YEAR
michael@0 1099 * field). That's because the ends of the representable range are at
michael@0 1100 * odd spots in the year. For calendars with the default Gregorian
michael@0 1101 * cutover, these limits are Sun Dec 02 16:47:04 GMT 292269055 BC to Sun
michael@0 1102 * Aug 17 07:12:55 GMT 292278994 AD, somewhat different for non-GMT
michael@0 1103 * zones. As a result, if the calendar is set to Aug 1 292278994 AD,
michael@0 1104 * the actual maximum of DAY_OF_MONTH is 17, not 30. If the date is Mar
michael@0 1105 * 31 in that year, the actual maximum month might be Jul, whereas is
michael@0 1106 * the date is Mar 15, the actual maximum might be Aug -- depending on
michael@0 1107 * the precise semantics that are desired. Similar considerations
michael@0 1108 * affect all fields. Nonetheless, this effect is sufficiently arcane
michael@0 1109 * that we permit it, rather than complicating the code to handle such
michael@0 1110 * intricacies. - liu 8/20/98
michael@0 1111
michael@0 1112 * UPDATE: No longer true, since we have pulled in the limit values on
michael@0 1113 * the year. - Liu 11/6/00 */
michael@0 1114
michael@0 1115 switch (field) {
michael@0 1116
michael@0 1117 case UCAL_YEAR:
michael@0 1118 /* The year computation is no different, in principle, from the
michael@0 1119 * others, however, the range of possible maxima is large. In
michael@0 1120 * addition, the way we know we've exceeded the range is different.
michael@0 1121 * For these reasons, we use the special case code below to handle
michael@0 1122 * this field.
michael@0 1123 *
michael@0 1124 * The actual maxima for YEAR depend on the type of calendar:
michael@0 1125 *
michael@0 1126 * Gregorian = May 17, 292275056 BC - Aug 17, 292278994 AD
michael@0 1127 * Julian = Dec 2, 292269055 BC - Jan 3, 292272993 AD
michael@0 1128 * Hybrid = Dec 2, 292269055 BC - Aug 17, 292278994 AD
michael@0 1129 *
michael@0 1130 * We know we've exceeded the maximum when either the month, date,
michael@0 1131 * time, or era changes in response to setting the year. We don't
michael@0 1132 * check for month, date, and time here because the year and era are
michael@0 1133 * sufficient to detect an invalid year setting. NOTE: If code is
michael@0 1134 * added to check the month and date in the future for some reason,
michael@0 1135 * Feb 29 must be allowed to shift to Mar 1 when setting the year.
michael@0 1136 */
michael@0 1137 {
michael@0 1138 if(U_FAILURE(status)) return 0;
michael@0 1139 Calendar *cal = clone();
michael@0 1140 if(!cal) {
michael@0 1141 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 1142 return 0;
michael@0 1143 }
michael@0 1144
michael@0 1145 cal->setLenient(TRUE);
michael@0 1146
michael@0 1147 int32_t era = cal->get(UCAL_ERA, status);
michael@0 1148 UDate d = cal->getTime(status);
michael@0 1149
michael@0 1150 /* Perform a binary search, with the invariant that lowGood is a
michael@0 1151 * valid year, and highBad is an out of range year.
michael@0 1152 */
michael@0 1153 int32_t lowGood = kGregorianCalendarLimits[UCAL_YEAR][1];
michael@0 1154 int32_t highBad = kGregorianCalendarLimits[UCAL_YEAR][2]+1;
michael@0 1155 while ((lowGood + 1) < highBad) {
michael@0 1156 int32_t y = (lowGood + highBad) / 2;
michael@0 1157 cal->set(UCAL_YEAR, y);
michael@0 1158 if (cal->get(UCAL_YEAR, status) == y && cal->get(UCAL_ERA, status) == era) {
michael@0 1159 lowGood = y;
michael@0 1160 } else {
michael@0 1161 highBad = y;
michael@0 1162 cal->setTime(d, status); // Restore original fields
michael@0 1163 }
michael@0 1164 }
michael@0 1165
michael@0 1166 delete cal;
michael@0 1167 return lowGood;
michael@0 1168 }
michael@0 1169
michael@0 1170 default:
michael@0 1171 return Calendar::getActualMaximum(field,status);
michael@0 1172 }
michael@0 1173 }
michael@0 1174
michael@0 1175
michael@0 1176 int32_t GregorianCalendar::handleGetExtendedYear() {
michael@0 1177 // the year to return
michael@0 1178 int32_t year = kEpochYear;
michael@0 1179
michael@0 1180 // year field to use
michael@0 1181 int32_t yearField = UCAL_EXTENDED_YEAR;
michael@0 1182
michael@0 1183 // There are three separate fields which could be used to
michael@0 1184 // derive the proper year. Use the one most recently set.
michael@0 1185 if (fStamp[yearField] < fStamp[UCAL_YEAR])
michael@0 1186 yearField = UCAL_YEAR;
michael@0 1187 if (fStamp[yearField] < fStamp[UCAL_YEAR_WOY])
michael@0 1188 yearField = UCAL_YEAR_WOY;
michael@0 1189
michael@0 1190 // based on the "best" year field, get the year
michael@0 1191 switch(yearField) {
michael@0 1192 case UCAL_EXTENDED_YEAR:
michael@0 1193 year = internalGet(UCAL_EXTENDED_YEAR, kEpochYear);
michael@0 1194 break;
michael@0 1195
michael@0 1196 case UCAL_YEAR:
michael@0 1197 {
michael@0 1198 // The year defaults to the epoch start, the era to AD
michael@0 1199 int32_t era = internalGet(UCAL_ERA, AD);
michael@0 1200 if (era == BC) {
michael@0 1201 year = 1 - internalGet(UCAL_YEAR, 1); // Convert to extended year
michael@0 1202 } else {
michael@0 1203 year = internalGet(UCAL_YEAR, kEpochYear);
michael@0 1204 }
michael@0 1205 }
michael@0 1206 break;
michael@0 1207
michael@0 1208 case UCAL_YEAR_WOY:
michael@0 1209 year = handleGetExtendedYearFromWeekFields(internalGet(UCAL_YEAR_WOY), internalGet(UCAL_WEEK_OF_YEAR));
michael@0 1210 #if defined (U_DEBUG_CAL)
michael@0 1211 // if(internalGet(UCAL_YEAR_WOY) != year) {
michael@0 1212 fprintf(stderr, "%s:%d: hGEYFWF[%d,%d] -> %d\n",
michael@0 1213 __FILE__, __LINE__,internalGet(UCAL_YEAR_WOY),internalGet(UCAL_WEEK_OF_YEAR),year);
michael@0 1214 //}
michael@0 1215 #endif
michael@0 1216 break;
michael@0 1217
michael@0 1218 default:
michael@0 1219 year = kEpochYear;
michael@0 1220 }
michael@0 1221 return year;
michael@0 1222 }
michael@0 1223
michael@0 1224 int32_t GregorianCalendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy)
michael@0 1225 {
michael@0 1226 // convert year to extended form
michael@0 1227 int32_t era = internalGet(UCAL_ERA, AD);
michael@0 1228 if(era == BC) {
michael@0 1229 yearWoy = 1 - yearWoy;
michael@0 1230 }
michael@0 1231 return Calendar::handleGetExtendedYearFromWeekFields(yearWoy, woy);
michael@0 1232 }
michael@0 1233
michael@0 1234
michael@0 1235 // -------------------------------------
michael@0 1236
michael@0 1237 UBool
michael@0 1238 GregorianCalendar::inDaylightTime(UErrorCode& status) const
michael@0 1239 {
michael@0 1240 if (U_FAILURE(status) || !getTimeZone().useDaylightTime())
michael@0 1241 return FALSE;
michael@0 1242
michael@0 1243 // Force an update of the state of the Calendar.
michael@0 1244 ((GregorianCalendar*)this)->complete(status); // cast away const
michael@0 1245
michael@0 1246 return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE);
michael@0 1247 }
michael@0 1248
michael@0 1249 // -------------------------------------
michael@0 1250
michael@0 1251 /**
michael@0 1252 * Return the ERA. We need a special method for this because the
michael@0 1253 * default ERA is AD, but a zero (unset) ERA is BC.
michael@0 1254 */
michael@0 1255 int32_t
michael@0 1256 GregorianCalendar::internalGetEra() const {
michael@0 1257 return isSet(UCAL_ERA) ? internalGet(UCAL_ERA) : (int32_t)AD;
michael@0 1258 }
michael@0 1259
michael@0 1260 const char *
michael@0 1261 GregorianCalendar::getType() const {
michael@0 1262 //static const char kGregorianType = "gregorian";
michael@0 1263
michael@0 1264 return "gregorian";
michael@0 1265 }
michael@0 1266
michael@0 1267 /**
michael@0 1268 * The system maintains a static default century start date and Year. They are
michael@0 1269 * initialized the first time they are used. Once the system default century date
michael@0 1270 * and year are set, they do not change.
michael@0 1271 */
michael@0 1272 static UDate gSystemDefaultCenturyStart = DBL_MIN;
michael@0 1273 static int32_t gSystemDefaultCenturyStartYear = -1;
michael@0 1274 static icu::UInitOnce gSystemDefaultCenturyInit = U_INITONCE_INITIALIZER;
michael@0 1275
michael@0 1276
michael@0 1277 UBool GregorianCalendar::haveDefaultCentury() const
michael@0 1278 {
michael@0 1279 return TRUE;
michael@0 1280 }
michael@0 1281
michael@0 1282 static void U_CALLCONV
michael@0 1283 initializeSystemDefaultCentury()
michael@0 1284 {
michael@0 1285 // initialize systemDefaultCentury and systemDefaultCenturyYear based
michael@0 1286 // on the current time. They'll be set to 80 years before
michael@0 1287 // the current time.
michael@0 1288 UErrorCode status = U_ZERO_ERROR;
michael@0 1289 GregorianCalendar calendar(status);
michael@0 1290 if (U_SUCCESS(status)) {
michael@0 1291 calendar.setTime(Calendar::getNow(), status);
michael@0 1292 calendar.add(UCAL_YEAR, -80, status);
michael@0 1293
michael@0 1294 gSystemDefaultCenturyStart = calendar.getTime(status);
michael@0 1295 gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status);
michael@0 1296 }
michael@0 1297 // We have no recourse upon failure unless we want to propagate the failure
michael@0 1298 // out.
michael@0 1299 }
michael@0 1300
michael@0 1301 UDate GregorianCalendar::defaultCenturyStart() const {
michael@0 1302 // lazy-evaluate systemDefaultCenturyStart
michael@0 1303 umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
michael@0 1304 return gSystemDefaultCenturyStart;
michael@0 1305 }
michael@0 1306
michael@0 1307 int32_t GregorianCalendar::defaultCenturyStartYear() const {
michael@0 1308 // lazy-evaluate systemDefaultCenturyStartYear
michael@0 1309 umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
michael@0 1310 return gSystemDefaultCenturyStartYear;
michael@0 1311 }
michael@0 1312
michael@0 1313 U_NAMESPACE_END
michael@0 1314
michael@0 1315 #endif /* #if !UCONFIG_NO_FORMATTING */
michael@0 1316
michael@0 1317 //eof

mercurial