hebrwcal.cpp (30797B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ****************************************************************************** 5 * Copyright (C) 2003-2016, International Business Machines Corporation 6 * and others. All Rights Reserved. 7 ****************************************************************************** 8 * 9 * File HEBRWCAL.CPP 10 * 11 * Modification History: 12 * 13 * Date Name Description 14 * 12/03/2003 srl ported from java HebrewCalendar 15 ***************************************************************************** 16 */ 17 18 #include "hebrwcal.h" 19 20 #if !UCONFIG_NO_FORMATTING 21 22 #include "cmemory.h" 23 #include "cstring.h" 24 #include "umutex.h" 25 #include <float.h> 26 #include "gregoimp.h" // ClockMath 27 #include "astro.h" // CalendarCache 28 #include "uhash.h" 29 #include "ucln_in.h" 30 31 // Hebrew Calendar implementation 32 33 /** 34 * The absolute date, in milliseconds since 1/1/1970 AD, Gregorian, 35 * of the start of the Hebrew calendar. In order to keep this calendar's 36 * time of day in sync with that of the Gregorian calendar, we use 37 * midnight, rather than sunset the day before. 38 */ 39 //static const double EPOCH_MILLIS = -180799862400000.; // 1/1/1 HY 40 41 static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { 42 // Minimum Greatest Least Maximum 43 // Minimum Maximum 44 { 0, 0, 0, 0}, // ERA 45 { -5000000, -5000000, 5000000, 5000000}, // YEAR 46 { 0, 0, 12, 12}, // MONTH 47 { 1, 1, 51, 56}, // WEEK_OF_YEAR 48 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH 49 { 1, 1, 29, 30}, // DAY_OF_MONTH 50 { 1, 1, 353, 385}, // DAY_OF_YEAR 51 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK 52 { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH 53 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM 54 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR 55 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY 56 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE 57 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND 58 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND 59 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET 60 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET 61 { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY 62 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL 63 { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR 64 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY 65 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY 66 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH 67 { 0, 0, 11, 12}, // ORDINAL_MONTH 68 }; 69 70 /** 71 * The lengths of the Hebrew months. This is complicated, because there 72 * are three different types of years, or six if you count leap years. 73 * Due to the rules for postponing the start of the year to avoid having 74 * certain holidays fall on the sabbath, the year can end up being three 75 * different lengths, called "deficient", "normal", and "complete". 76 */ 77 static const int8_t MONTH_LENGTH[][3] = { 78 // Deficient Normal Complete 79 { 30, 30, 30 }, //Tishri 80 { 29, 29, 30 }, //Heshvan 81 { 29, 30, 30 }, //Kislev 82 { 29, 29, 29 }, //Tevet 83 { 30, 30, 30 }, //Shevat 84 { 30, 30, 30 }, //Adar I (leap years only) 85 { 29, 29, 29 }, //Adar 86 { 30, 30, 30 }, //Nisan 87 { 29, 29, 29 }, //Iyar 88 { 30, 30, 30 }, //Sivan 89 { 29, 29, 29 }, //Tammuz 90 { 30, 30, 30 }, //Av 91 { 29, 29, 29 }, //Elul 92 }; 93 94 /** 95 * The cumulative # of days to the end of each month in a non-leap year 96 * Although this can be calculated from the MONTH_LENGTH table, 97 * keeping it around separately makes some calculations a lot faster 98 */ 99 100 static const int16_t MONTH_START[][3] = { 101 // Deficient Normal Complete 102 { 0, 0, 0 }, // (placeholder) 103 { 30, 30, 30 }, // Tishri 104 { 59, 59, 60 }, // Heshvan 105 { 88, 89, 90 }, // Kislev 106 { 117, 118, 119 }, // Tevet 107 { 147, 148, 149 }, // Shevat 108 { 147, 148, 149 }, // (Adar I placeholder) 109 { 176, 177, 178 }, // Adar 110 { 206, 207, 208 }, // Nisan 111 { 235, 236, 237 }, // Iyar 112 { 265, 266, 267 }, // Sivan 113 { 294, 295, 296 }, // Tammuz 114 { 324, 325, 326 }, // Av 115 { 353, 354, 355 }, // Elul 116 }; 117 118 /** 119 * The cumulative # of days to the end of each month in a leap year 120 */ 121 static const int16_t LEAP_MONTH_START[][3] = { 122 // Deficient Normal Complete 123 { 0, 0, 0 }, // (placeholder) 124 { 30, 30, 30 }, // Tishri 125 { 59, 59, 60 }, // Heshvan 126 { 88, 89, 90 }, // Kislev 127 { 117, 118, 119 }, // Tevet 128 { 147, 148, 149 }, // Shevat 129 { 177, 178, 179 }, // Adar I 130 { 206, 207, 208 }, // Adar II 131 { 236, 237, 238 }, // Nisan 132 { 265, 266, 267 }, // Iyar 133 { 295, 296, 297 }, // Sivan 134 { 324, 325, 326 }, // Tammuz 135 { 354, 355, 356 }, // Av 136 { 383, 384, 385 }, // Elul 137 }; 138 139 // There are 235 months in 19 years cycle. 140 static const int32_t MONTHS_IN_CYCLE = 235; 141 static const int32_t YEARS_IN_CYCLE = 19; 142 143 static icu::CalendarCache *gCache = nullptr; 144 145 U_CDECL_BEGIN 146 static UBool calendar_hebrew_cleanup() { 147 delete gCache; 148 gCache = nullptr; 149 return true; 150 } 151 U_CDECL_END 152 153 U_NAMESPACE_BEGIN 154 //------------------------------------------------------------------------- 155 // Constructors... 156 //------------------------------------------------------------------------- 157 158 /** 159 * Constructs a default <code>HebrewCalendar</code> using the current time 160 * in the default time zone with the default locale. 161 * @internal 162 */ 163 HebrewCalendar::HebrewCalendar(const Locale& aLocale, UErrorCode& success) 164 : Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success) 165 166 { 167 } 168 169 170 HebrewCalendar::~HebrewCalendar() { 171 } 172 173 const char *HebrewCalendar::getType() const { 174 return "hebrew"; 175 } 176 177 HebrewCalendar* HebrewCalendar::clone() const { 178 return new HebrewCalendar(*this); 179 } 180 181 HebrewCalendar::HebrewCalendar(const HebrewCalendar& other) : Calendar(other) { 182 } 183 184 185 //------------------------------------------------------------------------- 186 // Rolling and adding functions overridden from Calendar 187 // 188 // These methods call through to the default implementation in IBMCalendar 189 // for most of the fields and only handle the unusual ones themselves. 190 //------------------------------------------------------------------------- 191 192 /** 193 * Add a signed amount to a specified field, using this calendar's rules. 194 * For example, to add three days to the current date, you can call 195 * <code>add(Calendar.DATE, 3)</code>. 196 * <p> 197 * When adding to certain fields, the values of other fields may conflict and 198 * need to be changed. For example, when adding one to the {@link #MONTH MONTH} field 199 * for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field 200 * must be adjusted so that the result is "29 Elul 5758" rather than the invalid 201 * "30 Elul 5758". 202 * <p> 203 * This method is able to add to 204 * all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET}, 205 * and {@link #ZONE_OFFSET ZONE_OFFSET}. 206 * <p> 207 * <b>Note:</b> You should always use {@link #roll roll} and add rather 208 * than attempting to perform arithmetic operations directly on the fields 209 * of a <tt>HebrewCalendar</tt>. Since the {@link #MONTH MONTH} field behaves 210 * discontinuously in non-leap years, simple arithmetic can give invalid results. 211 * <p> 212 * @param field the time field. 213 * @param amount the amount to add to the field. 214 * 215 * @exception IllegalArgumentException if the field is invalid or refers 216 * to a field that cannot be handled by this method. 217 * @internal 218 */ 219 void HebrewCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) 220 { 221 if(U_FAILURE(status)) { 222 return; 223 } 224 switch (field) { 225 case UCAL_MONTH: 226 case UCAL_ORDINAL_MONTH: 227 { 228 // We can't just do a set(MONTH, get(MONTH) + amount). The 229 // reason is ADAR_1. Suppose amount is +2 and we land in 230 // ADAR_1 -- then we have to bump to ADAR_2 aka ADAR. But 231 // if amount is -2 and we land in ADAR_1, then we have to 232 // bump the other way -- down to SHEVAT. - Alan 11/00 233 int64_t month = get(UCAL_MONTH, status); 234 int32_t year = get(UCAL_YEAR, status); 235 UBool acrossAdar1; 236 if (amount > 0) { 237 acrossAdar1 = (month < ADAR_1); // started before ADAR_1? 238 month += amount; 239 // We know there are total 235 months in every 19 years. To speed 240 // up the iteration, we first fast forward in the multiple of 235 241 // months for 19 years before the iteration which check the leap year. 242 if (month >= MONTHS_IN_CYCLE) { 243 if (uprv_add32_overflow(year, (month / MONTHS_IN_CYCLE) * YEARS_IN_CYCLE, &year)) { 244 status = U_ILLEGAL_ARGUMENT_ERROR; 245 return; 246 } 247 month %= MONTHS_IN_CYCLE; 248 } 249 250 for (;;) { 251 if (acrossAdar1 && month>=ADAR_1 && !isLeapYear(year)) { 252 ++month; 253 } 254 if (month <= ELUL) { 255 break; 256 } 257 month -= ELUL+1; 258 ++year; 259 acrossAdar1 = true; 260 } 261 } else { 262 acrossAdar1 = (month > ADAR_1); // started after ADAR_1? 263 month += amount; 264 // We know there are total 235 months in every 19 years. To speed 265 // up the iteration, we first fast forward in the multiple of 235 266 // months for 19 years before the iteration which check the leap year. 267 if (month <= -MONTHS_IN_CYCLE) { 268 if (uprv_add32_overflow(year, (month / MONTHS_IN_CYCLE) * YEARS_IN_CYCLE, &year)) { 269 status = U_ILLEGAL_ARGUMENT_ERROR; 270 return; 271 } 272 month %= MONTHS_IN_CYCLE; 273 } 274 for (;;) { 275 if (acrossAdar1 && month<=ADAR_1 && !isLeapYear(year)) { 276 --month; 277 } 278 if (month >= 0) { 279 break; 280 } 281 month += ELUL+1; 282 --year; 283 acrossAdar1 = true; 284 } 285 } 286 set(UCAL_MONTH, month); 287 set(UCAL_YEAR, year); 288 pinField(UCAL_DAY_OF_MONTH, status); 289 break; 290 } 291 292 default: 293 Calendar::add(field, amount, status); 294 break; 295 } 296 } 297 298 /** 299 * @deprecated ICU 2.6 use UCalendarDateFields instead of EDateFields 300 */ 301 void HebrewCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) 302 { 303 add(static_cast<UCalendarDateFields>(field), amount, status); 304 } 305 306 namespace { 307 308 int32_t monthsInYear(int32_t year); 309 310 } // namespace 311 312 /** 313 * Rolls (up/down) a specified amount time on the given field. For 314 * example, to roll the current date up by three days, you can call 315 * <code>roll(Calendar.DATE, 3)</code>. If the 316 * field is rolled past its maximum allowable value, it will "wrap" back 317 * to its minimum and continue rolling. 318 * For example, calling <code>roll(Calendar.DATE, 10)</code> 319 * on a Hebrew calendar set to "25 Av 5758" will result in the date "5 Av 5758". 320 * <p> 321 * When rolling certain fields, the values of other fields may conflict and 322 * need to be changed. For example, when rolling the {@link #MONTH MONTH} field 323 * upward by one for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field 324 * must be adjusted so that the result is "29 Elul 5758" rather than the invalid 325 * "30 Elul". 326 * <p> 327 * This method is able to roll 328 * all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET}, 329 * and {@link #ZONE_OFFSET ZONE_OFFSET}. Subclasses may, of course, add support for 330 * additional fields in their overrides of <code>roll</code>. 331 * <p> 332 * <b>Note:</b> You should always use roll and {@link #add add} rather 333 * than attempting to perform arithmetic operations directly on the fields 334 * of a <tt>HebrewCalendar</tt>. Since the {@link #MONTH MONTH} field behaves 335 * discontinuously in non-leap years, simple arithmetic can give invalid results. 336 * <p> 337 * @param field the time field. 338 * @param amount the amount by which the field should be rolled. 339 * 340 * @exception IllegalArgumentException if the field is invalid or refers 341 * to a field that cannot be handled by this method. 342 * @internal 343 */ 344 void HebrewCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) 345 { 346 if(U_FAILURE(status)) { 347 return; 348 } 349 switch (field) { 350 case UCAL_MONTH: 351 case UCAL_ORDINAL_MONTH: 352 { 353 int32_t month = get(UCAL_MONTH, status); 354 int32_t year = get(UCAL_YEAR, status); 355 356 UBool leapYear = isLeapYear(year); 357 int32_t yearLength = monthsInYear(year); 358 int32_t newMonth = month + (amount % yearLength); 359 // 360 // If it's not a leap year and we're rolling past the missing month 361 // of ADAR_1, we need to roll an extra month to make up for it. 362 // 363 if (!leapYear) { 364 if (amount > 0 && month < ADAR_1 && newMonth >= ADAR_1) { 365 newMonth++; 366 } else if (amount < 0 && month > ADAR_1 && newMonth <= ADAR_1) { 367 newMonth--; 368 } 369 } 370 set(UCAL_MONTH, (newMonth + 13) % 13); 371 pinField(UCAL_DAY_OF_MONTH, status); 372 return; 373 } 374 default: 375 Calendar::roll(field, amount, status); 376 } 377 } 378 379 void HebrewCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { 380 roll(static_cast<UCalendarDateFields>(field), amount, status); 381 } 382 383 //------------------------------------------------------------------------- 384 // Support methods 385 //------------------------------------------------------------------------- 386 387 // Hebrew date calculations are performed in terms of days, hours, and 388 // "parts" (or halakim), which are 1/1080 of an hour, or 3 1/3 seconds. 389 static const int32_t HOUR_PARTS = 1080; 390 static const int32_t DAY_PARTS = 24*HOUR_PARTS; 391 392 // An approximate value for the length of a lunar month. 393 // It is used to calculate the approximate year and month of a given 394 // absolute date. 395 static const int32_t MONTH_DAYS = 29; 396 static const int32_t MONTH_FRACT = 12*HOUR_PARTS + 793; 397 static const int32_t MONTH_PARTS = MONTH_DAYS*DAY_PARTS + MONTH_FRACT; 398 399 // The time of the new moon (in parts) on 1 Tishri, year 1 (the epoch) 400 // counting from noon on the day before. BAHARAD is an abbreviation of 401 // Bet (Monday), Hey (5 hours from sunset), Resh-Daled (204). 402 static const int32_t BAHARAD = 11*HOUR_PARTS + 204; 403 404 namespace { 405 406 /** 407 * Finds the day # of the first day in the given Hebrew year. 408 * To do this, we want to calculate the time of the Tishri 1 new moon 409 * in that year. 410 * <p> 411 * The algorithm here is similar to ones described in a number of 412 * references, including: 413 * <ul> 414 * <li>"Calendrical Calculations", by Nachum Dershowitz & Edward Reingold, 415 * Cambridge University Press, 1997, pages 85-91. 416 * 417 * <li>Hebrew Calendar Science and Myths, 418 * <a href="http://www.geocities.com/Athens/1584/"> 419 * http://www.geocities.com/Athens/1584/</a> 420 * 421 * <li>The Calendar FAQ, 422 * <a href="http://www.faqs.org/faqs/calendars/faq/"> 423 * http://www.faqs.org/faqs/calendars/faq/</a> 424 * </ul> 425 */ 426 int32_t startOfYear(int32_t year, UErrorCode &status) 427 { 428 ucln_i18n_registerCleanup(UCLN_I18N_HEBREW_CALENDAR, calendar_hebrew_cleanup); 429 int64_t day = CalendarCache::get(&gCache, year, status); 430 if(U_FAILURE(status)) { 431 return 0; 432 } 433 434 if (day == 0) { 435 // # of months before year 436 int64_t months = ClockMath::floorDivideInt64( 437 (235LL * static_cast<int64_t>(year) - 234LL), 19LL); 438 439 int64_t frac = months * MONTH_FRACT + BAHARAD; // Fractional part of day # 440 day = months * 29LL + frac / DAY_PARTS; // Whole # part of calculation 441 frac = frac % DAY_PARTS; // Time of day 442 443 int32_t wd = (day % 7); // Day of week (0 == Monday) 444 445 if (wd == 2 || wd == 4 || wd == 6) { 446 // If the 1st is on Sun, Wed, or Fri, postpone to the next day 447 day += 1; 448 wd = (day % 7); 449 } else if (wd == 1 && frac > 15*HOUR_PARTS+204 && !HebrewCalendar::isLeapYear(year) ) { 450 // If the new moon falls after 3:11:20am (15h204p from the previous noon) 451 // on a Tuesday and it is not a leap year, postpone by 2 days. 452 // This prevents 356-day years. 453 day += 2; 454 } 455 else if (wd == 0 && frac > 21*HOUR_PARTS+589 && HebrewCalendar::isLeapYear(year-1) ) { 456 // If the new moon falls after 9:32:43 1/3am (21h589p from yesterday noon) 457 // on a Monday and *last* year was a leap year, postpone by 1 day. 458 // Prevents 382-day years. 459 day += 1; 460 } 461 if (day > INT32_MAX || day < INT32_MIN) { 462 status = U_ILLEGAL_ARGUMENT_ERROR; 463 return 0; 464 } 465 CalendarCache::put(&gCache, year, static_cast<int32_t>(day), status); 466 } 467 // Out of range value is alread rejected before putting into cache. 468 U_ASSERT(INT32_MIN <= day && day <= INT32_MAX); 469 return day; 470 } 471 472 int32_t daysInYear(int32_t eyear, UErrorCode& status) { 473 if (U_FAILURE(status)) { 474 return 0; 475 } 476 return startOfYear(eyear+1, status) - startOfYear(eyear, status); 477 } 478 479 /** 480 * Returns the type of a given year. 481 * 0 "Deficient" year with 353 or 383 days 482 * 1 "Normal" year with 354 or 384 days 483 * 2 "Complete" year with 355 or 385 days 484 */ 485 int32_t yearType(int32_t year, UErrorCode& status) 486 { 487 if (U_FAILURE(status)) { 488 return 0; 489 } 490 int32_t yearLength = daysInYear(year, status); 491 if (U_FAILURE(status)) { 492 return 0; 493 } 494 495 if (yearLength > 380) { 496 yearLength -= 30; // Subtract length of leap month. 497 } 498 499 int type = 0; 500 501 switch (yearLength) { 502 case 353: 503 type = 0; break; 504 case 354: 505 type = 1; break; 506 case 355: 507 type = 2; break; 508 default: 509 //throw new RuntimeException("Illegal year length " + yearLength + " in year " + year); 510 type = 1; 511 } 512 return type; 513 } 514 515 } // namespace 516 // 517 /** 518 * Determine whether a given Hebrew year is a leap year 519 * 520 * The rule here is that if (year % 19) == 0, 3, 6, 8, 11, 14, or 17. 521 * The formula below performs the same test, believe it or not. 522 */ 523 UBool HebrewCalendar::isLeapYear(int32_t year) { 524 //return (year * 12 + 17) % 19 >= 12; 525 int64_t x = (year*12LL + 17) % YEARS_IN_CYCLE; 526 return x >= ((x < 0) ? -7 : 12); 527 } 528 529 namespace{ 530 531 int32_t monthsInYear(int32_t year) { 532 return HebrewCalendar::isLeapYear(year) ? 13 : 12; 533 } 534 535 } // namespace 536 537 //------------------------------------------------------------------------- 538 // Calendar framework 539 //------------------------------------------------------------------------- 540 541 /** 542 * @internal 543 */ 544 int32_t HebrewCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { 545 return LIMITS[field][limitType]; 546 } 547 548 /** 549 * Returns the length of the given month in the given year 550 * @internal 551 */ 552 int32_t HebrewCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const { 553 if(U_FAILURE(status)) { 554 return 0; 555 } 556 // Resolve out-of-range months. This is necessary in order to 557 // obtain the correct year. We correct to 558 // a 12- or 13-month year (add/subtract 12 or 13, depending 559 // on the year) but since we _always_ number from 0..12, and 560 // the leap year determines whether or not month 5 (Adar 1) 561 // is present, we allow 0..12 in any given year. 562 while (month < 0) { 563 month += monthsInYear(--extendedYear); 564 } 565 // Careful: allow 0..12 in all years 566 while (month > 12) { 567 month -= monthsInYear(extendedYear++); 568 } 569 570 switch (month) { 571 case HESHVAN: 572 case KISLEV: 573 { 574 // These two month lengths can vary 575 int32_t type = yearType(extendedYear, status); 576 if(U_FAILURE(status)) { 577 return 0; 578 } 579 return MONTH_LENGTH[month][type]; 580 } 581 582 default: 583 // The rest are a fixed length 584 return MONTH_LENGTH[month][0]; 585 } 586 } 587 588 /** 589 * Returns the number of days in the given Hebrew year 590 * @internal 591 */ 592 int32_t HebrewCalendar::handleGetYearLength(int32_t eyear, UErrorCode& status) const { 593 return daysInYear(eyear, status); 594 } 595 596 void HebrewCalendar::validateField(UCalendarDateFields field, UErrorCode &status) { 597 if ((field == UCAL_MONTH || field == UCAL_ORDINAL_MONTH) 598 && !isLeapYear(handleGetExtendedYear(status)) && internalGetMonth(status) == ADAR_1) { 599 if (U_FAILURE(status)) { 600 return; 601 } 602 status = U_ILLEGAL_ARGUMENT_ERROR; 603 return; 604 } 605 Calendar::validateField(field, status); 606 } 607 //------------------------------------------------------------------------- 608 // Functions for converting from milliseconds to field values 609 //------------------------------------------------------------------------- 610 611 /** 612 * Subclasses may override this method to compute several fields 613 * specific to each calendar system. These are: 614 * 615 * <ul><li>ERA 616 * <li>YEAR 617 * <li>MONTH 618 * <li>DAY_OF_MONTH 619 * <li>DAY_OF_YEAR 620 * <li>EXTENDED_YEAR</ul> 621 * 622 * Subclasses can refer to the DAY_OF_WEEK and DOW_LOCAL fields, 623 * which will be set when this method is called. Subclasses can 624 * also call the getGregorianXxx() methods to obtain Gregorian 625 * calendar equivalents for the given Julian day. 626 * 627 * <p>In addition, subclasses should compute any subclass-specific 628 * fields, that is, fields from BASE_FIELD_COUNT to 629 * getFieldCount() - 1. 630 * @internal 631 */ 632 void HebrewCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { 633 if (U_FAILURE(status)) { 634 return; 635 } 636 int32_t d = julianDay - 347997; 637 double m = ClockMath::floorDivide((d * static_cast<double>(DAY_PARTS)), static_cast<double>(MONTH_PARTS)); // Months (approx) 638 int32_t year = static_cast<int32_t>(ClockMath::floorDivide((19. * m + 234.), 235.) + 1.); // Years (approx) 639 int32_t ys = startOfYear(year, status); // 1st day of year 640 if (U_FAILURE(status)) { 641 return; 642 } 643 int32_t dayOfYear = (d - ys); 644 645 // Because of the postponement rules, it's possible to guess wrong. Fix it. 646 while (dayOfYear < 1) { 647 year--; 648 ys = startOfYear(year, status); 649 if (U_FAILURE(status)) { 650 return; 651 } 652 dayOfYear = (d - ys); 653 } 654 655 // Now figure out which month we're in, and the date within that month 656 int32_t type = yearType(year, status); 657 if (U_FAILURE(status)) { 658 return; 659 } 660 UBool isLeap = isLeapYear(year); 661 662 int32_t month = 0; 663 int32_t momax = UPRV_LENGTHOF(MONTH_START); 664 while (month < momax && 665 dayOfYear > ( isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type] ) ) { 666 month++; 667 } 668 if (month >= momax || month<=0) { 669 // TODO: I found dayOfYear could be out of range when 670 // a large value is set to julianDay. I patched startOfYear 671 // to reduce the chace, but it could be still reproduced either 672 // by startOfYear or other places. For now, we check 673 // the month is in valid range to avoid out of array index 674 // access problem here. However, we need to carefully review 675 // the calendar implementation to check the extreme limit of 676 // each calendar field and the code works well for any values 677 // in the valid value range. -yoshito 678 status = U_ILLEGAL_ARGUMENT_ERROR; 679 return; 680 } 681 month--; 682 int dayOfMonth = dayOfYear - (isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type]); 683 684 internalSet(UCAL_ERA, 0); 685 // Check out of bound year 686 int32_t min_year = handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MINIMUM); 687 if (year < min_year) { 688 if (!isLenient()) { 689 status = U_ILLEGAL_ARGUMENT_ERROR; 690 return; 691 } 692 year = min_year; 693 } 694 int32_t max_year = handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MAXIMUM); 695 if (max_year < year) { 696 if (!isLenient()) { 697 status = U_ILLEGAL_ARGUMENT_ERROR; 698 return; 699 } 700 year = max_year; 701 } 702 internalSet(UCAL_YEAR, year); 703 internalSet(UCAL_EXTENDED_YEAR, year); 704 int32_t ordinal_month = month; 705 if (!isLeap && ordinal_month > ADAR_1) { 706 ordinal_month--; 707 } 708 internalSet(UCAL_ORDINAL_MONTH, ordinal_month); 709 internalSet(UCAL_MONTH, month); 710 internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); 711 internalSet(UCAL_DAY_OF_YEAR, dayOfYear); 712 } 713 714 //------------------------------------------------------------------------- 715 // Functions for converting from field values to milliseconds 716 //------------------------------------------------------------------------- 717 718 /** 719 * @internal 720 */ 721 int32_t HebrewCalendar::handleGetExtendedYear(UErrorCode& status ) { 722 if (U_FAILURE(status)) { 723 return 0; 724 } 725 if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { 726 return internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 727 } 728 return internalGet(UCAL_YEAR, 1); // Default to year 1 729 } 730 731 /** 732 * Return JD of start of given month/year. 733 * @internal 734 */ 735 int64_t HebrewCalendar::handleComputeMonthStart( 736 int32_t eyear, int32_t month, UBool /*useMonth*/, UErrorCode& status) const { 737 if (U_FAILURE(status)) { 738 return 0; 739 } 740 // Resolve out-of-range months. This is necessary in order to 741 // obtain the correct year. We correct to 742 // a 12- or 13-month year (add/subtract 12 or 13, depending 743 // on the year) but since we _always_ number from 0..12, and 744 // the leap year determines whether or not month 5 (Adar 1) 745 // is present, we allow 0..12 in any given year. 746 747 // The month could be in large value, we first roll 235 months to 19 years 748 // before the while loop. 749 if (month <= -MONTHS_IN_CYCLE || month >= MONTHS_IN_CYCLE) { 750 if (uprv_add32_overflow(eyear, (month / MONTHS_IN_CYCLE) * YEARS_IN_CYCLE, &eyear)) { 751 status = U_ILLEGAL_ARGUMENT_ERROR; 752 return 0; 753 } 754 month %= MONTHS_IN_CYCLE; 755 } 756 while (month < 0) { 757 if (uprv_add32_overflow(eyear, -1, &eyear) || 758 uprv_add32_overflow(month, monthsInYear(eyear), &month)) { 759 status = U_ILLEGAL_ARGUMENT_ERROR; 760 return 0; 761 } 762 } 763 // Careful: allow 0..12 in all years 764 while (month > 12) { 765 if (uprv_add32_overflow(month, -monthsInYear(eyear), &month) || 766 uprv_add32_overflow(eyear, 1, &eyear)) { 767 status = U_ILLEGAL_ARGUMENT_ERROR; 768 return 0; 769 } 770 } 771 772 int64_t day = startOfYear(eyear, status); 773 774 if(U_FAILURE(status)) { 775 return 0; 776 } 777 778 if (month != 0) { 779 int32_t type = yearType(eyear, status); 780 if (U_FAILURE(status)) { 781 return 0; 782 } 783 if (isLeapYear(eyear)) { 784 day += LEAP_MONTH_START[month][type]; 785 } else { 786 day += MONTH_START[month][type]; 787 } 788 } 789 790 return day + 347997LL; 791 } 792 793 IMPL_SYSTEM_DEFAULT_CENTURY(HebrewCalendar, "@calendar=hebrew") 794 795 bool HebrewCalendar::inTemporalLeapYear(UErrorCode& status) const { 796 if (U_FAILURE(status)) { 797 return false; 798 } 799 int32_t eyear = get(UCAL_EXTENDED_YEAR, status); 800 if (U_FAILURE(status)) { 801 return false; 802 } 803 return isLeapYear(eyear); 804 } 805 806 static const char * const gTemporalMonthCodesForHebrew[] = { 807 "M01", "M02", "M03", "M04", "M05", "M05L", "M06", 808 "M07", "M08", "M09", "M10", "M11", "M12", nullptr 809 }; 810 811 const char* HebrewCalendar::getTemporalMonthCode(UErrorCode& status) const { 812 int32_t month = get(UCAL_MONTH, status); 813 if (U_FAILURE(status)) { 814 return nullptr; 815 } 816 return gTemporalMonthCodesForHebrew[month]; 817 } 818 819 void HebrewCalendar::setTemporalMonthCode(const char* code, UErrorCode& status ) 820 { 821 if (U_FAILURE(status)) { 822 return; 823 } 824 int32_t len = static_cast<int32_t>(uprv_strlen(code)); 825 if (len == 3 || len == 4) { 826 for (int m = 0; gTemporalMonthCodesForHebrew[m] != nullptr; m++) { 827 if (uprv_strcmp(code, gTemporalMonthCodesForHebrew[m]) == 0) { 828 set(UCAL_MONTH, m); 829 return; 830 } 831 } 832 } 833 status = U_ILLEGAL_ARGUMENT_ERROR; 834 } 835 836 int32_t HebrewCalendar::internalGetMonth(UErrorCode& status) const { 837 if (U_FAILURE(status)) { 838 return 0; 839 } 840 if (resolveFields(kMonthPrecedence) == UCAL_ORDINAL_MONTH) { 841 int32_t ordinalMonth = internalGet(UCAL_ORDINAL_MONTH); 842 HebrewCalendar* nonConstThis = const_cast<HebrewCalendar*>(this); // cast away const 843 844 int32_t year = nonConstThis->handleGetExtendedYear(status); 845 if (U_FAILURE(status)) { 846 return 0; 847 } 848 if (isLeapYear(year) || ordinalMonth <= ADAR_1) { 849 return ordinalMonth; 850 } 851 if (!uprv_add32_overflow(ordinalMonth, 1, &ordinalMonth)) { 852 return ordinalMonth; 853 } 854 } 855 return Calendar::internalGetMonth(status); 856 } 857 858 int32_t HebrewCalendar::getRelatedYearDifference() const { 859 constexpr int32_t kHebrewCalendarRelatedYearDifference = -3760; 860 return kHebrewCalendarRelatedYearDifference; 861 } 862 863 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(HebrewCalendar) 864 865 U_NAMESPACE_END 866 867 #endif // UCONFIG_NO_FORMATTING