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