ICU-22743 Change internal functions to propagate errors up.

This commit is contained in:
Frank Tang 2024-03-28 11:39:35 -07:00 committed by Frank Yung-Fong Tang
parent 5e35ffc87e
commit 0c02f8c007
11 changed files with 372 additions and 183 deletions

View file

@ -3446,7 +3446,10 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCo
if(isSet(UCAL_DAY_OF_MONTH)) {
dayOfMonth = internalGet(UCAL_DAY_OF_MONTH,1);
} else {
dayOfMonth = getDefaultDayInMonth(year, month);
dayOfMonth = getDefaultDayInMonth(year, month, status);
if (U_FAILURE(status)) {
return 0;
}
}
if (uprv_add32_overflow(dayOfMonth, julianDay, &dayOfMonth)) {
status = U_ILLEGAL_ARGUMENT_ERROR;
@ -3654,7 +3657,7 @@ Calendar::getDefaultMonthInYear(int32_t /*eyear*/, UErrorCode& /* status */)
}
int32_t
Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/)
Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/, UErrorCode& /* status */)
{
return 1;
}

View file

@ -116,9 +116,9 @@ U_NAMESPACE_BEGIN
namespace {
const TimeZone* getAstronomerTimeZone();
int32_t newMoonNear(const TimeZone*, double, UBool);
int32_t newYear(const icu::ChineseCalendar::Setting&, int32_t);
UBool isLeapMonthBetween(const TimeZone*, int32_t, int32_t);
int32_t newMoonNear(const TimeZone*, double, UBool, UErrorCode&);
int32_t newYear(const icu::ChineseCalendar::Setting&, int32_t, UErrorCode&);
UBool isLeapMonthBetween(const TimeZone*, int32_t, int32_t, UErrorCode&);
} // namespace
@ -253,13 +253,16 @@ int32_t ChineseCalendar::handleGetExtendedYear(UErrorCode& status) {
*/
int32_t ChineseCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const {
const Setting setting = getSetting(status);
if (U_FAILURE(status)) {
return 0;
}
int32_t thisStart = handleComputeMonthStart(extendedYear, month, true, status);
if (U_FAILURE(status)) {
return 0;
}
thisStart = thisStart -
kEpochStartAsJulianDay + 1; // Julian day -> local days
int32_t nextStart = newMoonNear(setting.zoneAstroCalc, thisStart + SYNODIC_GAP, true);
int32_t nextStart = newMoonNear(setting.zoneAstroCalc, thisStart + SYNODIC_GAP, true, status);
return nextStart - thisStart;
}
@ -311,7 +314,7 @@ struct MonthInfo {
};
struct MonthInfo computeMonthInfo(
const icu::ChineseCalendar::Setting& setting,
int32_t gyear, int32_t days);
int32_t gyear, int32_t days, UErrorCode& status);
} // namespace
@ -353,8 +356,11 @@ int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
return 0;
}
int32_t theNewYear = newYear(setting, gyear);
int32_t newMoon = newMoonNear(setting.zoneAstroCalc, theNewYear + month * 29, true);
int32_t theNewYear = newYear(setting, gyear, status);
int32_t newMoon = newMoonNear(setting.zoneAstroCalc, theNewYear + month * 29, true, status);
if (U_FAILURE(status)) {
return 0;
}
// Ignore IS_LEAP_MONTH field if useMonth is false
bool isLeapMonth = false;
@ -368,9 +374,15 @@ int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
int32_t unusedDayOfYear;
Grego::dayToFields(newMoon, gyear, unusedMonth, unusedDayOfWeek, unusedDayOfMonth, unusedDayOfYear);
struct MonthInfo monthInfo = computeMonthInfo(setting, gyear, newMoon);
struct MonthInfo monthInfo = computeMonthInfo(setting, gyear, newMoon, status);
if (U_FAILURE(status)) {
return 0;
}
if (month != monthInfo.month-1 || isLeapMonth != monthInfo.isLeapMonth) {
newMoon = newMoonNear(setting.zoneAstroCalc, newMoon + SYNODIC_GAP, true);
newMoon = newMoonNear(setting.zoneAstroCalc, newMoon + SYNODIC_GAP, true, status);
if (U_FAILURE(status)) {
return 0;
}
}
int32_t julianDay;
if (uprv_add32_overflow(newMoon-1, kEpochStartAsJulianDay, &julianDay)) {
@ -450,10 +462,16 @@ struct RollMonthInfo rollMonth(const TimeZone* timeZone, int32_t amount, int32_t
// otherwise it will be the start of month 1.
int prevMoon = output.thisMoon -
(int) (CalendarAstronomer::SYNODIC_MONTH * (month - 0.5));
prevMoon = newMoonNear(timeZone, prevMoon, true);
if (isLeapMonthBetween(timeZone, prevMoon, output.thisMoon)) {
prevMoon = newMoonNear(timeZone, prevMoon, true, status);
if (U_FAILURE(status)) {
return output;
}
if (isLeapMonthBetween(timeZone, prevMoon, output.thisMoon, status)) {
++month;
}
if (U_FAILURE(status)) {
return output;
}
}
}
// Now do the standard roll computation on month, with the
@ -488,8 +506,9 @@ void ChineseCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode
int32_t dayOfMonth = get(UCAL_DAY_OF_MONTH, status);
bool isLeapMonth = get(UCAL_IS_LEAP_MONTH, status) == 1;
if (U_FAILURE(status)) break;
struct RollMonthInfo r = rollMonth(setting.zoneAstroCalc, amount,
day, month, dayOfMonth, isLeapMonth, hasLeapMonthBetweenWinterSolstices, status);
struct RollMonthInfo r = rollMonth(
setting.zoneAstroCalc, amount, day, month, dayOfMonth, isLeapMonth,
hasLeapMonthBetweenWinterSolstices, status);
if (U_FAILURE(status)) break;
if (r.newMoon != r.month) {
offsetMonth(r.thisMoon, dayOfMonth, r.newMoon - r.month, status);
@ -525,15 +544,18 @@ namespace {
* @param days days after January 1, 1970 0:00 in the astronomical base zone
* @return milliseconds after January 1, 1970 0:00 GMT
*/
double daysToMillis(const TimeZone* timeZone, double days) {
double daysToMillis(const TimeZone* timeZone, double days, UErrorCode& status) {
if (U_FAILURE(status)) {
return 0;
}
double millis = days * (double)kOneDay;
if (timeZone != nullptr) {
int32_t rawOffset, dstOffset;
UErrorCode status = U_ZERO_ERROR;
timeZone->getOffset(millis, false, rawOffset, dstOffset, status);
if (U_SUCCESS(status)) {
return millis - (double)(rawOffset + dstOffset);
if (U_FAILURE(status)) {
return 0;
}
return millis - (double)(rawOffset + dstOffset);
}
return millis - (double)CHINA_OFFSET;
}
@ -544,14 +566,17 @@ double daysToMillis(const TimeZone* timeZone, double days) {
* @param millis milliseconds after January 1, 1970 0:00 GMT
* @return days after January 1, 1970 0:00 in the astronomical base zone
*/
double millisToDays(const TimeZone* timeZone, double millis) {
double millisToDays(const TimeZone* timeZone, double millis, UErrorCode& status) {
if (U_FAILURE(status)) {
return 0;
}
if (timeZone != nullptr) {
int32_t rawOffset, dstOffset;
UErrorCode status = U_ZERO_ERROR;
timeZone->getOffset(millis, false, rawOffset, dstOffset, status);
if (U_SUCCESS(status)) {
return ClockMath::floorDivide(millis + (double)(rawOffset + dstOffset), kOneDay);
if (U_FAILURE(status)) {
return 0;
}
return ClockMath::floorDivide(millis + (double)(rawOffset + dstOffset), kOneDay);
}
return ClockMath::floorDivide(millis + (double)CHINA_OFFSET, kOneDay);
}
@ -571,23 +596,35 @@ double millisToDays(const TimeZone* timeZone, double millis) {
* winter solstice of the given year
*/
int32_t winterSolstice(const icu::ChineseCalendar::Setting& setting,
int32_t gyear) {
int32_t gyear, UErrorCode& status) {
if (U_FAILURE(status)) {
return 0;
}
const TimeZone* timeZone = setting.zoneAstroCalc;
UErrorCode status = U_ZERO_ERROR;
int32_t cacheValue = CalendarCache::get(setting.winterSolsticeCache, gyear, status);
if (U_FAILURE(status)) {
return 0;
}
if (cacheValue == 0) {
// In books December 15 is used, but it fails for some years
// using our algorithms, e.g.: 1298 1391 1492 1553 1560. That
// is, winterSolstice(1298) starts search at Dec 14 08:00:00
// PST 1298 with a final result of Dec 14 10:31:59 PST 1299.
double ms = daysToMillis(timeZone, Grego::fieldsToDay(gyear, UCAL_DECEMBER, 1));
double ms = daysToMillis(timeZone, Grego::fieldsToDay(gyear, UCAL_DECEMBER, 1), status);
if (U_FAILURE(status)) {
return 0;
}
// Winter solstice is 270 degrees solar longitude aka Dongzhi
double days = millisToDays(timeZone,
CalendarAstronomer(ms)
.getSunTime(CalendarAstronomer::WINTER_SOLSTICE(), true));
.getSunTime(CalendarAstronomer::WINTER_SOLSTICE(), true),
status);
if (U_FAILURE(status)) {
return 0;
}
if (days < INT32_MIN || days > INT32_MAX) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
@ -608,14 +645,23 @@ int32_t winterSolstice(const icu::ChineseCalendar::Setting& setting,
* @param days days after January 1, 1970 0:00 Asia/Shanghai
* @param after if true, search for a new moon on or after the given
* date; otherwise, search for a new moon before it
* @param status
* @return days after January 1, 1970 0:00 Asia/Shanghai of the nearest
* new moon after or before <code>days</code>
*/
int32_t newMoonNear(const TimeZone* timeZone, double days, UBool after) {
int32_t newMoonNear(const TimeZone* timeZone, double days, UBool after, UErrorCode& status) {
if (U_FAILURE(status)) {
return 0;
}
double ms = daysToMillis(timeZone, days, status);
if (U_FAILURE(status)) {
return 0;
}
return (int32_t) millisToDays(
timeZone,
CalendarAstronomer(daysToMillis(timeZone, days))
.getMoonTime(CalendarAstronomer::NEW_MOON(), after));
CalendarAstronomer(ms)
.getMoonTime(CalendarAstronomer::NEW_MOON(), after),
status);
}
/**
@ -637,10 +683,20 @@ int32_t synodicMonthsBetween(int32_t day1, int32_t day2) {
* @param timeZone time zone for the Astro calculation.
* @param days days after January 1, 1970 0:00 Asia/Shanghai
*/
int32_t majorSolarTerm(const TimeZone* timeZone, int32_t days) {
int32_t majorSolarTerm(const TimeZone* timeZone, int32_t days, UErrorCode& status) {
if (U_FAILURE(status)) {
return 0;
}
// Compute (floor(solarLongitude / (pi/6)) + 2) % 12
int32_t term = ( ((int32_t)(6 * CalendarAstronomer(daysToMillis(timeZone, days))
double ms = daysToMillis(timeZone, days, status);
if (U_FAILURE(status)) {
return 0;
}
int32_t term = ( ((int32_t)(6 * CalendarAstronomer(ms)
.getSunLongitude() / CalendarAstronomer::PI)) + 2 ) % 12;
if (U_FAILURE(status)) {
return 0;
}
if (term < 1) {
term += 12;
}
@ -653,9 +709,17 @@ int32_t majorSolarTerm(const TimeZone* timeZone, int32_t days) {
* @param newMoon days after January 1, 1970 0:00 Asia/Shanghai of a new
* moon
*/
UBool hasNoMajorSolarTerm(const TimeZone* timeZone, int32_t newMoon) {
return majorSolarTerm(timeZone, newMoon) ==
majorSolarTerm(timeZone, newMoonNear(timeZone, newMoon + SYNODIC_GAP, true));
UBool hasNoMajorSolarTerm(const TimeZone* timeZone, int32_t newMoon, UErrorCode& status) {
if (U_FAILURE(status)) {
return false;
}
int32_t term1 = majorSolarTerm(timeZone, newMoon, status);
int32_t term2 = majorSolarTerm(
timeZone, newMoonNear(timeZone, newMoon + SYNODIC_GAP, true, status), status);
if (U_FAILURE(status)) {
return false;
}
return term1 == term2;
}
@ -672,7 +736,10 @@ UBool hasNoMajorSolarTerm(const TimeZone* timeZone, int32_t newMoon) {
* @param newMoon2 days after January 1, 1970 0:00 astronomical base zone
* of a new moon
*/
UBool isLeapMonthBetween(const TimeZone* timeZone, int32_t newMoon1, int32_t newMoon2) {
UBool isLeapMonthBetween(const TimeZone* timeZone, int32_t newMoon1, int32_t newMoon2, UErrorCode& status) {
if (U_FAILURE(status)) {
return false;
}
#ifdef U_DEBUG_CHNSECAL
// This is only needed to debug the timeOfAngle divergence bug.
@ -685,10 +752,13 @@ UBool isLeapMonthBetween(const TimeZone* timeZone, int32_t newMoon1, int32_t new
#endif
while (newMoon2 >= newMoon1) {
if (hasNoMajorSolarTerm(timeZone, newMoon2)) {
if (hasNoMajorSolarTerm(timeZone, newMoon2, status)) {
return true;
}
newMoon2 = newMoonNear(timeZone, newMoon2 - SYNODIC_GAP, false);
newMoon2 = newMoonNear(timeZone, newMoon2 - SYNODIC_GAP, false, status);
if (U_FAILURE(status)) {
return false;
}
}
return false;
}
@ -704,39 +774,63 @@ UBool isLeapMonthBetween(const TimeZone* timeZone, int32_t newMoon1, int32_t new
*/
struct MonthInfo computeMonthInfo(
const icu::ChineseCalendar::Setting& setting,
int32_t gyear, int32_t days) {
struct MonthInfo output;
int32_t gyear, int32_t days, UErrorCode& status) {
struct MonthInfo output = {0, 0, 0, false, false};
if (U_FAILURE(status)) {
return output;
}
// Find the winter solstices before and after the target date.
// These define the boundaries of this Chinese year, specifically,
// the position of month 11, which always contains the solstice.
// We want solsticeBefore <= date < solsticeAfter.
int32_t solsticeBefore;
int32_t solsticeAfter = winterSolstice(setting, gyear);
int32_t solsticeAfter = winterSolstice(setting, gyear, status);
if (U_FAILURE(status)) {
return output;
}
if (days < solsticeAfter) {
solsticeBefore = winterSolstice(setting, gyear - 1);
solsticeBefore = winterSolstice(setting, gyear - 1, status);
} else {
solsticeBefore = solsticeAfter;
solsticeAfter = winterSolstice(setting, gyear + 1);
solsticeAfter = winterSolstice(setting, gyear + 1, status);
}
if (U_FAILURE(status)) {
return output;
}
const TimeZone* timeZone = setting.zoneAstroCalc;
// Find the start of the month after month 11. This will be either
// the prior month 12 or leap month 11 (very rare). Also find the
// start of the following month 11.
int32_t firstMoon = newMoonNear(timeZone, solsticeBefore + 1, true);
int32_t lastMoon = newMoonNear(timeZone, solsticeAfter + 1, false);
output.thisMoon = newMoonNear(timeZone, days + 1, false); // Start of this month
int32_t firstMoon = newMoonNear(timeZone, solsticeBefore + 1, true, status);
int32_t lastMoon = newMoonNear(timeZone, solsticeAfter + 1, false, status);
if (U_FAILURE(status)) {
return output;
}
output.thisMoon = newMoonNear(timeZone, days + 1, false, status); // Start of this month
if (U_FAILURE(status)) {
return output;
}
output.hasLeapMonthBetweenWinterSolstices = synodicMonthsBetween(firstMoon, lastMoon) == 12;
output.month = synodicMonthsBetween(firstMoon, output.thisMoon);
int32_t theNewYear = newYear(setting, gyear);
int32_t theNewYear = newYear(setting, gyear, status);
if (U_FAILURE(status)) {
return output;
}
if (days < theNewYear) {
theNewYear = newYear(setting, gyear-1);
theNewYear = newYear(setting, gyear-1, status);
if (U_FAILURE(status)) {
return output;
}
}
if (output.hasLeapMonthBetweenWinterSolstices &&
isLeapMonthBetween(timeZone, firstMoon, output.thisMoon)) {
isLeapMonthBetween(timeZone, firstMoon, output.thisMoon, status)) {
output.month--;
}
if (U_FAILURE(status)) {
return output;
}
if (output.month < 1) {
output.month += 12;
}
@ -745,9 +839,13 @@ struct MonthInfo computeMonthInfo(
output.ordinalMonth += 12;
}
output.isLeapMonth = output.hasLeapMonthBetweenWinterSolstices &&
hasNoMajorSolarTerm(timeZone, output.thisMoon) &&
hasNoMajorSolarTerm(timeZone, output.thisMoon, status) &&
!isLeapMonthBetween(timeZone, firstMoon,
newMoonNear(timeZone, output.thisMoon - SYNODIC_GAP, false));
newMoonNear(timeZone, output.thisMoon - SYNODIC_GAP, false, status),
status);
if (U_FAILURE(status)) {
return output;
}
return output;
}
@ -787,7 +885,10 @@ void ChineseCalendar::handleComputeFields(int32_t julianDay, UErrorCode & status
if (U_FAILURE(status)) {
return;
}
struct MonthInfo monthInfo = computeMonthInfo(setting, gyear, days);
struct MonthInfo monthInfo = computeMonthInfo(setting, gyear, days, status);
if (U_FAILURE(status)) {
return;
}
hasLeapMonthBetweenWinterSolstices = monthInfo.hasLeapMonthBetweenWinterSolstices;
// Extended year and cycle year is based on the epoch year
@ -808,9 +909,15 @@ void ChineseCalendar::handleComputeFields(int32_t julianDay, UErrorCode & status
// date is in month 11, leap 11, 12. There is never a leap 12.
// New year computations are cached so this should be cheap in
// the long run.
int32_t theNewYear = newYear(setting, gyear);
int32_t theNewYear = newYear(setting, gyear, status);
if (U_FAILURE(status)) {
return;
}
if (days < theNewYear) {
theNewYear = newYear(setting, gyear-1);
theNewYear = newYear(setting, gyear-1, status);
}
if (U_FAILURE(status)) {
return;
}
cycle++;
yearOfCycle++;
@ -858,26 +965,37 @@ namespace {
* Chinese new year of the given year (this will be a new moon)
*/
int32_t newYear(const icu::ChineseCalendar::Setting& setting,
int32_t gyear) {
int32_t gyear, UErrorCode& status) {
if (U_FAILURE(status)) {
return 0;
}
const TimeZone* timeZone = setting.zoneAstroCalc;
UErrorCode status = U_ZERO_ERROR;
int32_t cacheValue = CalendarCache::get(setting.newYearCache, gyear, status);
if (U_FAILURE(status)) {
return 0;
}
if (cacheValue == 0) {
int32_t solsticeBefore= winterSolstice(setting, gyear - 1);
int32_t solsticeAfter = winterSolstice(setting, gyear);
int32_t newMoon1 = newMoonNear(timeZone, solsticeBefore + 1, true);
int32_t newMoon2 = newMoonNear(timeZone, newMoon1 + SYNODIC_GAP, true);
int32_t newMoon11 = newMoonNear(timeZone, solsticeAfter + 1, false);
int32_t solsticeBefore= winterSolstice(setting, gyear - 1, status);
int32_t solsticeAfter = winterSolstice(setting, gyear, status);
int32_t newMoon1 = newMoonNear(timeZone, solsticeBefore + 1, true, status);
int32_t newMoon2 = newMoonNear(timeZone, newMoon1 + SYNODIC_GAP, true, status);
int32_t newMoon11 = newMoonNear(timeZone, solsticeAfter + 1, false, status);
if (U_FAILURE(status)) {
return 0;
}
if (synodicMonthsBetween(newMoon1, newMoon11) == 12 &&
(hasNoMajorSolarTerm(timeZone, newMoon1) ||
hasNoMajorSolarTerm(timeZone, newMoon2))) {
cacheValue = newMoonNear(timeZone, newMoon2 + SYNODIC_GAP, true);
(hasNoMajorSolarTerm(timeZone, newMoon1, status) ||
hasNoMajorSolarTerm(timeZone, newMoon2, status))) {
cacheValue = newMoonNear(timeZone, newMoon2 + SYNODIC_GAP, true, status);
} else {
cacheValue = newMoon2;
}
if (U_FAILURE(status)) {
return 0;
}
CalendarCache::put(setting.newYearCache, gyear, cacheValue, status);
}
@ -904,7 +1022,9 @@ int32_t newYear(const icu::ChineseCalendar::Setting& setting,
void ChineseCalendar::offsetMonth(int32_t newMoon, int32_t dayOfMonth, int32_t delta,
UErrorCode& status) {
const Setting setting = getSetting(status);
if (U_FAILURE(status)) { return; }
if (U_FAILURE(status)) {
return;
}
// Move to the middle of the month before our target month.
double value = newMoon;
@ -917,7 +1037,10 @@ void ChineseCalendar::offsetMonth(int32_t newMoon, int32_t dayOfMonth, int32_t d
newMoon = static_cast<int32_t>(value);
// Search forward to the target month's new moon
newMoon = newMoonNear(setting.zoneAstroCalc, newMoon, true);
newMoon = newMoonNear(setting.zoneAstroCalc, newMoon, true, status);
if (U_FAILURE(status)) {
return;
}
// Find the target dayOfMonth
int32_t jd = newMoon + kEpochStartAsJulianDay - 1 + dayOfMonth;

View file

@ -304,6 +304,12 @@ void HebrewCalendar::add(EDateFields field, int32_t amount, UErrorCode& status)
add((UCalendarDateFields)field, amount, status);
}
namespace {
int32_t monthsInYear(int32_t year);
} // namespace
/**
* Rolls (up/down) a specified amount time on the given field. For
* example, to roll the current date up by three days, you can call
@ -396,6 +402,8 @@ static const int32_t MONTH_PARTS = MONTH_DAYS*DAY_PARTS + MONTH_FRACT;
// Bet (Monday), Hey (5 hours from sunset), Resh-Daled (204).
static const int32_t BAHARAD = 11*HOUR_PARTS + 204;
namespace {
/**
* Finds the day # of the first day in the given Hebrew year.
* To do this, we want to calculate the time of the Tishri 1 new moon
@ -416,7 +424,7 @@ static const int32_t BAHARAD = 11*HOUR_PARTS + 204;
* http://www.faqs.org/faqs/calendars/faq/</a>
* </ul>
*/
int32_t HebrewCalendar::startOfYear(int32_t year, UErrorCode &status)
int32_t startOfYear(int32_t year, UErrorCode &status)
{
ucln_i18n_registerCleanup(UCLN_I18N_HEBREW_CALENDAR, calendar_hebrew_cleanup);
int64_t day = CalendarCache::get(&gCache, year, status);
@ -440,13 +448,13 @@ int32_t HebrewCalendar::startOfYear(int32_t year, UErrorCode &status)
day += 1;
wd = (day % 7);
}
if (wd == 1 && frac > 15*HOUR_PARTS+204 && !isLeapYear(year) ) {
if (wd == 1 && frac > 15*HOUR_PARTS+204 && !HebrewCalendar::isLeapYear(year) ) {
// If the new moon falls after 3:11:20am (15h204p from the previous noon)
// on a Tuesday and it is not a leap year, postpone by 2 days.
// This prevents 356-day years.
day += 2;
}
else if (wd == 0 && frac > 21*HOUR_PARTS+589 && isLeapYear(year-1) ) {
else if (wd == 0 && frac > 21*HOUR_PARTS+589 && HebrewCalendar::isLeapYear(year-1) ) {
// If the new moon falls after 9:32:43 1/3am (21h589p from yesterday noon)
// on a Monday and *last* year was a leap year, postpone by 1 day.
// Prevents 382-day years.
@ -463,16 +471,11 @@ int32_t HebrewCalendar::startOfYear(int32_t year, UErrorCode &status)
return day;
}
/**
* Find the day of the week for a given day
*
* @param day The # of days since the start of the Hebrew calendar,
* 1-based (i.e. 1/1/1 AM is day 1).
*/
int32_t HebrewCalendar::absoluteDayToDayOfWeek(int32_t day)
{
// We know that 1/1/1 AM is a Monday, which makes the math easy...
return (day % 7) + 1;
int32_t daysInYear(int32_t eyear, UErrorCode& status) {
if (U_FAILURE(status)) {
return 0;
}
return startOfYear(eyear+1, status) - startOfYear(eyear, status);
}
/**
@ -481,9 +484,15 @@ int32_t HebrewCalendar::absoluteDayToDayOfWeek(int32_t day)
* 1 "Normal" year with 354 or 384 days
* 2 "Complete" year with 355 or 385 days
*/
int32_t HebrewCalendar::yearType(int32_t year) const
int32_t yearType(int32_t year, UErrorCode& status)
{
int32_t yearLength = handleGetYearLength(year);
if (U_FAILURE(status)) {
return 0;
}
int32_t yearLength = daysInYear(year, status);
if (U_FAILURE(status)) {
return 0;
}
if (yearLength > 380) {
yearLength -= 30; // Subtract length of leap month.
@ -505,6 +514,8 @@ int32_t HebrewCalendar::yearType(int32_t year) const
return type;
}
} // namespace
//
/**
* Determine whether a given Hebrew year is a leap year
*
@ -517,10 +528,14 @@ UBool HebrewCalendar::isLeapYear(int32_t year) {
return x >= ((x < 0) ? -7 : 12);
}
int32_t HebrewCalendar::monthsInYear(int32_t year) {
return isLeapYear(year) ? 13 : 12;
namespace{
int32_t monthsInYear(int32_t year) {
return HebrewCalendar::isLeapYear(year) ? 13 : 12;
}
} // namespace
//-------------------------------------------------------------------------
// Calendar framework
//-------------------------------------------------------------------------
@ -557,8 +572,14 @@ int32_t HebrewCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month
switch (month) {
case HESHVAN:
case KISLEV:
// These two month lengths can vary
return MONTH_LENGTH[month][yearType(extendedYear)];
{
// These two month lengths can vary
int32_t type = yearType(extendedYear, status);
if(U_FAILURE(status)) {
return 0;
}
return MONTH_LENGTH[month][type];
}
default:
// The rest are a fixed length
@ -572,7 +593,11 @@ int32_t HebrewCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month
*/
int32_t HebrewCalendar::handleGetYearLength(int32_t eyear) const {
UErrorCode status = U_ZERO_ERROR;
return startOfYear(eyear+1, status) - startOfYear(eyear, status);
int32_t len = daysInYear(eyear, status);
if (U_FAILURE(status)) {
return 12;
}
return len;
}
void HebrewCalendar::validateField(UCalendarDateFields field, UErrorCode &status) {
@ -635,12 +660,16 @@ void HebrewCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status)
}
// Now figure out which month we're in, and the date within that month
int32_t type = yearType(year);
int32_t type = yearType(year, status);
if (U_FAILURE(status)) {
return;
}
UBool isLeap = isLeapYear(year);
int32_t month = 0;
int32_t momax = UPRV_LENGTHOF(MONTH_START);
while (month < momax && dayOfYear > ( isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type] ) ) {
while (month < momax &&
dayOfYear > ( isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type] ) ) {
month++;
}
if (month >= momax || month<=0) {
@ -663,25 +692,25 @@ void HebrewCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status)
// Check out of bound year
int32_t min_year = handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MINIMUM);
if (year < min_year) {
if (!isLenient()) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
year = min_year;
if (!isLenient()) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
year = min_year;
}
int32_t max_year = handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MAXIMUM);
if (max_year < year) {
if (!isLenient()) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
year = max_year;
if (!isLenient()) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
year = max_year;
}
internalSet(UCAL_YEAR, year);
internalSet(UCAL_EXTENDED_YEAR, year);
int32_t ordinal_month = month;
if (!isLeap && ordinal_month > ADAR_1) {
ordinal_month--;
ordinal_month--;
}
internalSet(UCAL_ORDINAL_MONTH, ordinal_month);
internalSet(UCAL_MONTH, month);
@ -754,10 +783,14 @@ int64_t HebrewCalendar::handleComputeMonthStart(
}
if (month != 0) {
int32_t type = yearType(eyear, status);
if (U_FAILURE(status)) {
return 0;
}
if (isLeapYear(eyear)) {
day += LEAP_MONTH_START[month][yearType(eyear)];
day += LEAP_MONTH_START[month][type];
} else {
day += MONTH_START[month][yearType(eyear)];
day += MONTH_START[month][type];
}
}

View file

@ -329,6 +329,7 @@ public:
* @stable ICU 2.0
*/
virtual int32_t handleGetYearLength(int32_t eyear) const override;
/**
* Subclasses may override this method to compute several fields
* specific to each calendar system. These are:
@ -427,44 +428,6 @@ public:
protected:
virtual int32_t internalGetMonth(UErrorCode& status) const override;
private: // Calendar-specific implementation
/**
* Finds the day # of the first day in the given Hebrew year.
* To do this, we want to calculate the time of the Tishri 1 new moon
* in that year.
* <p>
* The algorithm here is similar to ones described in a number of
* references, including:
* <ul>
* <li>"Calendrical Calculations", by Nachum Dershowitz & Edward Reingold,
* Cambridge University Press, 1997, pages 85-91.
*
* <li>Hebrew Calendar Science and Myths,
* <a href="http://www.geocities.com/Athens/1584/">
* http://www.geocities.com/Athens/1584/</a>
*
* <li>The Calendar FAQ,
* <a href="http://www.faqs.org/faqs/calendars/faq/">
* http://www.faqs.org/faqs/calendars/faq/</a>
* </ul>
* @param year extended year
* @return day number (JD)
* @internal
*/
static int32_t startOfYear(int32_t year, UErrorCode& status);
static int32_t absoluteDayToDayOfWeek(int32_t day) ;
/**
* @internal
*/
int32_t yearType(int32_t year) const;
/**
* @internal
*/
static int32_t monthsInYear(int32_t year) ;
};
U_NAMESPACE_END

View file

@ -303,7 +303,7 @@ inline bool civilLeapYear(int32_t year) {
return (14 + 11 * year) % 30 < 11;
}
int32_t trueMonthStart(int32_t month);
int32_t trueMonthStart(int32_t month, UErrorCode& status);
} // namespace
@ -311,8 +311,8 @@ int32_t trueMonthStart(int32_t month);
* Return the day # on which the given year starts. Days are counted
* from the Hijri epoch, origin 0.
*/
int64_t IslamicCalendar::yearStart(int32_t year) const{
return trueMonthStart(12*(year-1));
int64_t IslamicCalendar::yearStart(int32_t year, UErrorCode& status) const {
return trueMonthStart(12*(year-1), status);
}
/**
@ -334,7 +334,7 @@ int64_t IslamicCalendar::monthStart(int32_t year, int32_t month, UErrorCode& sta
return 0;
}
return trueMonthStart(month);
return trueMonthStart(month, status);
}
namespace {
@ -357,9 +357,11 @@ double moonAge(UDate time);
*
* @return The day number on which the given month starts.
*/
int32_t trueMonthStart(int32_t month) {
int32_t trueMonthStart(int32_t month, UErrorCode& status) {
if (U_FAILURE(status)) {
return 0;
}
ucln_i18n_registerCleanup(UCLN_I18N_ISLAMIC_CALENDAR, calendar_islamic_cleanup);
UErrorCode status = U_ZERO_ERROR;
int64_t start = CalendarCache::get(&gMonthCache, month, status);
if (U_SUCCESS(status) && start==0) {
@ -417,18 +419,40 @@ double moonAge(UDate time) {
* @draft ICU 2.4
*/
int32_t IslamicCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month,
UErrorCode& /* status */) const {
UErrorCode& status) const {
month = 12*(extendedYear-1) + month;
return trueMonthStart(month+1) - trueMonthStart(month) ;
int32_t len = trueMonthStart(month+1, status) - trueMonthStart(month, status) ;
if (U_FAILURE(status)) {
return 0;
}
return len;
}
namespace {
int32_t yearLength(int32_t extendedYear, UErrorCode& status) {
int32_t month = 12*(extendedYear-1);
int32_t length = trueMonthStart(month + 12, status) - trueMonthStart(month, status);
if (U_FAILURE(status)) {
return 0;
}
return length;
}
} // namepsace
/**
* Return the number of days in the given Islamic year
* @draft ICU 2.4
*/
int32_t IslamicCalendar::handleGetYearLength(int32_t extendedYear) const {
int32_t month = 12*(extendedYear-1);
return (trueMonthStart(month + 12) - trueMonthStart(month));
UErrorCode status = U_ZERO_ERROR;
int32_t length = yearLength(extendedYear, status);
if (U_FAILURE(status)) {
// fallback to normal Islamic calendar length 355 day a year if we
// encounter error and cannot propagate.
return 355;
}
return length;
}
//-------------------------------------------------------------------------
@ -500,7 +524,9 @@ int32_t IslamicCalendar::handleGetExtendedYear(UErrorCode& /* status */) {
* @draft ICU 2.4
*/
void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) {
if (U_FAILURE(status)) return;
if (U_FAILURE(status)) {
return;
}
int32_t days = julianDay - getEpoc();
// Guess at the number of elapsed full months since the epoch
@ -516,10 +542,16 @@ void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status)
// Find out the last time that the new moon was actually visible at this longitude
// This returns midnight the night that the moon was visible at sunset.
while ((startDate = trueMonthStart(month)) > days) {
while ((startDate = trueMonthStart(month, status)) > days) {
if (U_FAILURE(status)) {
return;
}
// If it was after the date in question, back up a month and try again
month--;
}
if (U_FAILURE(status)) {
return;
}
int32_t year = month >= 0 ? ((month / 12) + 1) : ((month + 1 ) / 12);
month = ((month % 12) + 12 ) % 12;
@ -636,7 +668,7 @@ IslamicCivilCalendar* IslamicCivilCalendar::clone() const {
* Return the day # on which the given year starts. Days are counted
* from the Hijri epoch, origin 0.
*/
int64_t IslamicCivilCalendar::yearStart(int32_t year) const{
int64_t IslamicCivilCalendar::yearStart(int32_t year, UErrorCode& /* status */) const {
return 354LL * (year-1LL) + ClockMath::floorDivideInt64(3 + 11LL * year, 30LL);
}
@ -696,14 +728,19 @@ int32_t IslamicCivilCalendar::handleGetYearLength(int32_t extendedYear) const {
* @draft ICU 2.4
*/
void IslamicCivilCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) {
if (U_FAILURE(status)) return;
if (U_FAILURE(status)) {
return;
}
int32_t days = julianDay - getEpoc();
// Use the civil calendar approximation, which is just arithmetic
int64_t year =
ClockMath::floorDivideInt64(30LL * days + 10646LL, 10631LL);
int32_t month = static_cast<int32_t>(
uprv_ceil((days - 29 - yearStart(year)) / 29.5 ));
uprv_ceil((days - 29 - yearStart(year, status)) / 29.5 ));
if (U_FAILURE(status)) {
return;
}
month = month<11?month:11;
int64_t dayOfMonth = (days - monthStart(year, month, status)) + 1;
@ -781,9 +818,9 @@ IslamicUmalquraCalendar* IslamicUmalquraCalendar::clone() const {
* Return the day # on which the given year starts. Days are counted
* from the Hijri epoch, origin 0.
*/
int64_t IslamicUmalquraCalendar::yearStart(int32_t year) const {
int64_t IslamicUmalquraCalendar::yearStart(int32_t year, UErrorCode& status) const {
if (year < UMALQURA_YEAR_START || year > UMALQURA_YEAR_END) {
return IslamicCivilCalendar::yearStart(year);
return IslamicCivilCalendar::yearStart(year, status);
}
year -= UMALQURA_YEAR_START;
// rounded least-squares fit of the dates previously calculated from UMALQURA_MONTHLENGTH iteration
@ -801,10 +838,10 @@ int64_t IslamicUmalquraCalendar::yearStart(int32_t year) const {
* @param month The hijri month, 0-based (assumed to be in range 0..11)
*/
int64_t IslamicUmalquraCalendar::monthStart(int32_t year, int32_t month, UErrorCode& status) const {
int64_t ms = yearStart(year, status);
if (U_FAILURE(status)) {
return 0;
}
int64_t ms = yearStart(year);
for(int i=0; i< month; i++){
ms+= handleGetMonthLength(year, i, status);
if (U_FAILURE(status)) {
@ -834,20 +871,32 @@ int32_t IslamicUmalquraCalendar::handleGetMonthLength(int32_t extendedYear, int3
return length;
}
int32_t IslamicUmalquraCalendar::yearLength(int32_t extendedYear, UErrorCode& status) const {
if (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END) {
return IslamicCivilCalendar::handleGetYearLength(extendedYear);
}
int length = 0;
for(int i=0; i<12; i++) {
length += handleGetMonthLength(extendedYear, i, status);
if (U_FAILURE(status)) {
return 0;
}
}
return length;
}
/**
* Return the number of days in the given Islamic year
* @draft ICU 2.4
*/
int32_t IslamicUmalquraCalendar::handleGetYearLength(int32_t extendedYear) const {
if (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END) {
return IslamicCivilCalendar::handleGetYearLength(extendedYear);
UErrorCode status = U_ZERO_ERROR;
int32_t length = yearLength(extendedYear, status);
if (U_FAILURE(status)) {
// fallback to normal Islamic calendar length 355 day a year if we
// encounter error and cannot propagate.
return 355;
}
int length = 0;
UErrorCode internalStatus = U_ZERO_ERROR;
for(int i=0; i<12; i++) {
length += handleGetMonthLength(extendedYear, i, internalStatus);
}
U_ASSERT(U_SUCCESS(internalStatus));
return length;
}
@ -868,12 +917,17 @@ int32_t IslamicUmalquraCalendar::handleGetYearLength(int32_t extendedYear) const
* @draft ICU 2.4
*/
void IslamicUmalquraCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) {
if (U_FAILURE(status)) return;
if (U_FAILURE(status)) {
return;
}
int64_t year;
int32_t month;
int32_t days = julianDay - getEpoc();
static int64_t kUmalquraStart = yearStart(UMALQURA_YEAR_START);
static int64_t kUmalquraStart = yearStart(UMALQURA_YEAR_START, status);
if (U_FAILURE(status)) {
return;
}
if (days < kUmalquraStart) {
IslamicCivilCalendar::handleComputeFields(julianDay, status);
return;
@ -886,13 +940,16 @@ void IslamicUmalquraCalendar::handleComputeFields(int32_t julianDay, UErrorCode
int32_t d = 1;
// need a slight correction to some
while (d > 0) {
d = days - yearStart(++year) + 1;
int32_t yearLength = handleGetYearLength(year);
if (d == yearLength) {
d = days - yearStart(++year, status) + 1;
int32_t length = yearLength(year, status);
if (U_FAILURE(status)) {
return;
}
if (d == length) {
month = 11;
break;
}
if (d < yearLength){
if (d < length){
int32_t monthLen = handleGetMonthLength(year, month, status);
for (month = 0;
d > monthLen;

View file

@ -201,7 +201,7 @@ class U_I18N_API IslamicCalendar : public Calendar {
* Return the day # on which the given year starts. Days are counted
* from the Hijri epoch, origin 0.
*/
virtual int64_t yearStart(int32_t year) const;
virtual int64_t yearStart(int32_t year, UErrorCode& status) const;
/**
* Return the day # on which the given month starts. Days are counted
@ -413,7 +413,7 @@ class U_I18N_API IslamicCivilCalendar : public IslamicCalendar {
* from the Hijri epoch, origin 0.
* @internal
*/
virtual int64_t yearStart(int32_t year) const override;
virtual int64_t yearStart(int32_t year, UErrorCode& status) const override;
/**
* Return the day # on which the given month starts. Days are counted
@ -596,7 +596,7 @@ class U_I18N_API IslamicUmalquraCalendar : public IslamicCivilCalendar {
* from the Hijri epoch, origin 0.
* @internal
*/
virtual int64_t yearStart(int32_t year) const override;
virtual int64_t yearStart(int32_t year, UErrorCode& status) const override;
/**
* Return the day # on which the given month starts. Days are counted
@ -640,6 +640,9 @@ class U_I18N_API IslamicUmalquraCalendar : public IslamicCivilCalendar {
* @internal
*/
virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override;
private:
virtual int32_t yearLength(int32_t extendedYear, UErrorCode& status) const;
};

View file

@ -171,15 +171,19 @@ int32_t JapaneseCalendar::getDefaultMonthInYear(int32_t eyear, UErrorCode& statu
return month;
}
int32_t JapaneseCalendar::getDefaultDayInMonth(int32_t eyear, int32_t month)
int32_t JapaneseCalendar::getDefaultDayInMonth(int32_t eyear, int32_t month, UErrorCode& status)
{
if (U_FAILURE(status)) {
return 0;
}
int32_t era = internalGetEra();
int32_t day = 1;
int32_t eraStart[3] = { 0,0,0 };
UErrorCode status = U_ZERO_ERROR;
gJapaneseEraRules->getStartDate(era, eraStart, status);
U_ASSERT(U_SUCCESS(status));
if (U_FAILURE(status)) {
return 0;
}
if (eyear == eraStart[0] && (month == eraStart[1] - 1)) {
return eraStart[2];
}

View file

@ -195,6 +195,7 @@ protected:
* taking year and era into account. Will return the first month of the given era, if
* the current year is an ascension year.
* @param eyear the extended year
* @param status Indicates the status.
* @internal
*/
virtual int32_t getDefaultMonthInYear(int32_t eyear, UErrorCode& status) override;
@ -205,9 +206,10 @@ protected:
* era, if the current month is an ascension year and month.
* @param eyear the extended year
* @param mon the month in the year
* @param status Indicates the status.
* @internal
*/
virtual int32_t getDefaultDayInMonth(int32_t eyear, int32_t month) override;
virtual int32_t getDefaultDayInMonth(int32_t eyear, int32_t month, UErrorCode& status) override;
virtual bool isEra0CountingBackward() const override { return false; }
};

View file

@ -1576,10 +1576,9 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
case UDAT_MONTH_FIELD:
case UDAT_STANDALONE_MONTH_FIELD:
if (uprv_strcmp(cal.getType(),"hebrew") == 0) {
HebrewCalendar *hc = (HebrewCalendar*)&cal;
if (hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value == 6 && count >= 3 )
if (HebrewCalendar::isLeapYear(cal.get(UCAL_YEAR,status)) && value == 6 && count >= 3 )
value = 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
if (!hc->isLeapYear(hc->get(UCAL_YEAR,status)) && value >= 6 && count < 3 )
if (!HebrewCalendar::isLeapYear(cal.get(UCAL_YEAR,status)) && value >= 6 && count < 3 )
value--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
}
{

View file

@ -2045,9 +2045,11 @@ protected:
* taking currently-set year and era into account. Defaults to 1 for Gregorian.
* @param eyear the extended year
* @param month the month in the year
* @param status Output param set to failure code on function return
* when this function fails.
* @internal
*/
virtual int32_t getDefaultDayInMonth(int32_t eyear, int32_t month);
virtual int32_t getDefaultDayInMonth(int32_t eyear, int32_t month, UErrorCode& status);
//-------------------------------------------------------------------------
// Protected utility methods for use by subclasses. These are very handy

View file

@ -5651,7 +5651,7 @@ void CalendarTest::Test22633ChineseOverflow() {
U_ASSERT(U_SUCCESS(status));
cal->set(UCAL_EXTENDED_YEAR, -1594662558);
cal->get(UCAL_YEAR, status);
assertTrue("Should return success", U_SUCCESS(status));
assertTrue("Should return failure", U_FAILURE(status));
cal->setTime(17000065021099877464213620139773683829419175940649608600213244013003611130029599692535053209683880603725167923910423116397083334648012657787978113960494455603744210944.000000, status);
cal->add(UCAL_YEAR, 1935762034, status);