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