1/* 2******************************************************************************* 3* Copyright (C) 1997-2012, 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 60static const int16_t kNumDays[] 61= {0,31,59,90,120,151,181,212,243,273,304,334}; // 0-based, for day-in-year 62static const int16_t kLeapNumDays[] 63= {0,31,60,91,121,152,182,213,244,274,305,335}; // 0-based, for day-in-year 64static const int8_t kMonthLength[] 65= {31,28,31,30,31,30,31,31,30,31,30,31}; // 0-based 66static 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 76static 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 136U_NAMESPACE_BEGIN 137 138UOBJECT_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; 146static const uint32_t kCutoverJulianDay = 2299161; 147static const UDate kPapalCutover = (2299161.0 - kEpochStartAsJulianDay) * U_MILLIS_PER_DAY; 148//static const UDate kPapalCutoverJulian = (2299161.0 - kEpochStartAsJulianDay); 149 150// ------------------------------------- 151 152GregorianCalendar::GregorianCalendar(UErrorCode& status) 153: Calendar(status), 154fGregorianCutover(kPapalCutover), 155fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 156fIsGregorian(TRUE), fInvertGregorian(FALSE) 157{ 158 setTimeInMillis(getNow(), status); 159} 160 161// ------------------------------------- 162 163GregorianCalendar::GregorianCalendar(TimeZone* zone, UErrorCode& status) 164: Calendar(zone, Locale::getDefault(), status), 165fGregorianCutover(kPapalCutover), 166fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 167fIsGregorian(TRUE), fInvertGregorian(FALSE) 168{ 169 setTimeInMillis(getNow(), status); 170} 171 172// ------------------------------------- 173 174GregorianCalendar::GregorianCalendar(const TimeZone& zone, UErrorCode& status) 175: Calendar(zone, Locale::getDefault(), status), 176fGregorianCutover(kPapalCutover), 177fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 178fIsGregorian(TRUE), fInvertGregorian(FALSE) 179{ 180 setTimeInMillis(getNow(), status); 181} 182 183// ------------------------------------- 184 185GregorianCalendar::GregorianCalendar(const Locale& aLocale, UErrorCode& status) 186: Calendar(TimeZone::createDefault(), aLocale, status), 187fGregorianCutover(kPapalCutover), 188fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 189fIsGregorian(TRUE), fInvertGregorian(FALSE) 190{ 191 setTimeInMillis(getNow(), status); 192} 193 194// ------------------------------------- 195 196GregorianCalendar::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 208GregorianCalendar::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 220GregorianCalendar::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 235GregorianCalendar::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 252GregorianCalendar::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 271GregorianCalendar::~GregorianCalendar() 272{ 273} 274 275// ------------------------------------- 276 277GregorianCalendar::GregorianCalendar(const GregorianCalendar &source) 278: Calendar(source), 279fGregorianCutover(source.fGregorianCutover), 280fCutoverJulianDay(source.fCutoverJulianDay), fNormalizedGregorianCutover(source.fNormalizedGregorianCutover), fGregorianCutoverYear(source.fGregorianCutoverYear), 281fIsGregorian(source.fIsGregorian), fInvertGregorian(source.fInvertGregorian) 282{ 283} 284 285// ------------------------------------- 286 287Calendar* GregorianCalendar::clone() const 288{ 289 return new GregorianCalendar(*this); 290} 291 292// ------------------------------------- 293 294GregorianCalendar & 295GregorianCalendar::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 310UBool GregorianCalendar::isEquivalentTo(const Calendar& other) const 311{ 312 // Calendar override. 313 return Calendar::isEquivalentTo(other) && 314 fGregorianCutover == ((GregorianCalendar*)&other)->fGregorianCutover; 315} 316 317// ------------------------------------- 318 319void 320GregorianCalendar::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 366void GregorianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) { 367 int32_t eyear, month, dayOfMonth, dayOfYear, unusedRemainder; 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.0*julianEpochDay) + 1464.0, (int32_t) 1461, unusedRemainder); 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 451UDate 452GregorianCalendar::getGregorianChange() const 453{ 454 return fGregorianCutover; 455} 456 457// ------------------------------------- 458 459UBool 460GregorianCalendar::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 471int32_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 529int32_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 576int32_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 587int32_t GregorianCalendar::handleGetYearLength(int32_t eyear) const { 588 return isLeapYear(eyear) ? 366 : 365; 589} 590 591 592int32_t 593GregorianCalendar::monthLength(int32_t month) const 594{ 595 int32_t year = internalGet(UCAL_EXTENDED_YEAR); 596 return handleGetMonthLength(year, month); 597} 598 599// ------------------------------------- 600 601int32_t 602GregorianCalendar::monthLength(int32_t month, int32_t year) const 603{ 604 return isLeapYear(year) ? kLeapMonthLength[month] : kMonthLength[month]; 605} 606 607// ------------------------------------- 608 609int32_t 610GregorianCalendar::yearLength(int32_t year) const 611{ 612 return isLeapYear(year) ? 366 : 365; 613} 614 615// ------------------------------------- 616 617int32_t 618GregorianCalendar::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*/ 631void 632GregorianCalendar::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 643UBool 644GregorianCalendar::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 684UBool 685GregorianCalendar::boundsCheck(int32_t value, UCalendarDateFields field) const 686{ 687 return value >= getMinimum(field) && value <= getMaximum(field); 688} 689 690// ------------------------------------- 691 692UDate 693GregorianCalendar::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*/ 714double 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 785double 786GregorianCalendar::millisToJulianDay(UDate millis) 787{ 788 return (double)kEpochStartAsJulianDay + ClockMath::floorDivide(millis, (double)kOneDay); 789} 790 791// ------------------------------------- 792 793UDate 794GregorianCalendar::julianDayToMillis(double julian) 795{ 796 return (UDate) ((julian - kEpochStartAsJulianDay) * (double) kOneDay); 797} 798 799// ------------------------------------- 800 801int32_t 802GregorianCalendar::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 816void 817GregorianCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { 818 roll((UCalendarDateFields) field, amount, status); 819} 820 821void 822GregorianCalendar::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*/ 1051int32_t GregorianCalendar::getActualMinimum(EDateFields field) const 1052{ 1053 return getMinimum((UCalendarDateFields)field); 1054} 1055 1056int32_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*/ 1068int32_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*/ 1083int32_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*/ 1094int32_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 1176int32_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 1224int32_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 1237UBool 1238GregorianCalendar::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*/ 1255int32_t 1256GregorianCalendar::internalGetEra() const { 1257 return isSet(UCAL_ERA) ? internalGet(UCAL_ERA) : (int32_t)AD; 1258} 1259 1260const char * 1261GregorianCalendar::getType() const { 1262 //static const char kGregorianType = "gregorian"; 1263 1264 return "gregorian"; 1265} 1266 1267const UDate GregorianCalendar::fgSystemDefaultCentury = DBL_MIN; 1268const int32_t GregorianCalendar::fgSystemDefaultCenturyYear = -1; 1269 1270UDate GregorianCalendar::fgSystemDefaultCenturyStart = DBL_MIN; 1271int32_t GregorianCalendar::fgSystemDefaultCenturyStartYear = -1; 1272 1273 1274UBool GregorianCalendar::haveDefaultCentury() const 1275{ 1276 return TRUE; 1277} 1278 1279UDate GregorianCalendar::defaultCenturyStart() const 1280{ 1281 return internalGetDefaultCenturyStart(); 1282} 1283 1284int32_t GregorianCalendar::defaultCenturyStartYear() const 1285{ 1286 return internalGetDefaultCenturyStartYear(); 1287} 1288 1289UDate 1290GregorianCalendar::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 1306int32_t 1307GregorianCalendar::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 1323void 1324GregorianCalendar::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 1352U_NAMESPACE_END 1353 1354#endif /* #if !UCONFIG_NO_FORMATTING */ 1355 1356//eof 1357