Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ICU-22513 Return error if days is too large in IslamicUmalquraCalendar #2680

Merged
merged 1 commit into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions icu4c/source/i18n/calendar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3078,7 +3078,10 @@ void Calendar::computeTime(UErrorCode& status) {
}

// Compute the Julian day
int32_t julianDay = computeJulianDay();
int32_t julianDay = computeJulianDay(status);
if (U_FAILURE(status)) {
return;
}

double millis = Grego::julianDayToMillis(julianDay);

Expand Down Expand Up @@ -3309,7 +3312,7 @@ int32_t Calendar::computeZoneOffset(double millis, double millisInDay, UErrorCod
return rawOffset + dstOffset;
}

int32_t Calendar::computeJulianDay()
int32_t Calendar::computeJulianDay(UErrorCode &status)
{
// We want to see if any of the date fields is newer than the
// JULIAN_DAY. If not, then we use JULIAN_DAY. If so, then we do
Expand All @@ -3333,12 +3336,15 @@ int32_t Calendar::computeJulianDay()
bestField = UCAL_DAY_OF_MONTH;
}

return handleComputeJulianDay(bestField);
return handleComputeJulianDay(bestField, status);
}

// -------------------------------------------

int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField) {
int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCode &status) {
if (U_FAILURE(status)) {
return 0;
}
UBool useMonth = (bestField == UCAL_DAY_OF_MONTH ||
bestField == UCAL_WEEK_OF_MONTH ||
bestField == UCAL_DAY_OF_WEEK_IN_MONTH);
Expand All @@ -3351,6 +3357,12 @@ int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField) {
}

internalSet(UCAL_EXTENDED_YEAR, year);
// Return U_ILLEGAL_ARGUMENT_ERROR if year is too large that may cuase int32_t overflow
// later.
if (year > INT32_MAX / 400) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}

#if defined (U_DEBUG_CAL)
fprintf(stderr, "%s:%d: bestField= %s - y=%d\n", __FILE__, __LINE__, fldName(bestField), year);
Expand Down
14 changes: 10 additions & 4 deletions icu4c/source/i18n/gregocal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,17 +472,20 @@ GregorianCalendar::isLeapYear(int32_t year) const

// -------------------------------------

int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField)
int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCode& status)
{
fInvertGregorian = false;

int32_t jd = Calendar::handleComputeJulianDay(bestField);
int32_t jd = Calendar::handleComputeJulianDay(bestField, status);
if (U_FAILURE(status)) {
return 0;
}

if((bestField == UCAL_WEEK_OF_YEAR) && // if we are doing WOY calculations, we are counting relative to Jan 1 *julian*
(internalGet(UCAL_EXTENDED_YEAR)==fGregorianCutoverYear) &&
jd >= fCutoverJulianDay) {
fInvertGregorian = true; // So that the Julian Jan 1 will be used in handleComputeMonthStart
return Calendar::handleComputeJulianDay(bestField);
return Calendar::handleComputeJulianDay(bestField, status);
}


Expand All @@ -495,7 +498,10 @@ int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField)
__FILE__, __LINE__, jd);
#endif
fInvertGregorian = true;
jd = Calendar::handleComputeJulianDay(bestField);
jd = Calendar::handleComputeJulianDay(bestField, status);
if (U_FAILURE(status)) {
return 0;
}
#if defined (U_DEBUG_CAL)
fprintf(stderr, "%s:%d: fIsGregorian %s, fInvertGregorian %s - ",
__FILE__, __LINE__,fIsGregorian?"T":"F", fInvertGregorian?"T":"F");
Expand Down
6 changes: 4 additions & 2 deletions icu4c/source/i18n/unicode/calendar.h
Original file line number Diff line number Diff line change
Expand Up @@ -1704,10 +1704,11 @@ class U_I18N_API Calendar : public UObject {
* handleGetMonthLength() to obtain the calendar-specific month
* length.
* @param bestField which field to use to calculate the date
* @param status ICU Error Code
* @return julian day specified by calendar fields.
* @internal
*/
virtual int32_t handleComputeJulianDay(UCalendarDateFields bestField);
virtual int32_t handleComputeJulianDay(UCalendarDateFields bestField, UErrorCode &status);

/**
* Subclasses must override this to convert from week fields
Expand All @@ -1731,10 +1732,11 @@ class U_I18N_API Calendar : public UObject {
/**
* Compute the Julian day from fields. Will determine whether to use
* the JULIAN_DAY field directly, or other fields.
* @param status ICU Error Code
* @return the julian day
* @internal
*/
int32_t computeJulianDay();
int32_t computeJulianDay(UErrorCode &status);

/**
* Compute the milliseconds in the day from the fields. This is a
Expand Down
3 changes: 2 additions & 1 deletion icu4c/source/i18n/unicode/gregocal.h
Original file line number Diff line number Diff line change
Expand Up @@ -495,10 +495,11 @@ class U_I18N_API GregorianCalendar: public Calendar {
* handleGetMonthLength() to obtain the calendar-specific month
* length.
* @param bestField which field to use to calculate the date
* @param status Fill-in parameter which receives the status of this operation.
* @return julian day specified by calendar fields.
* @internal
*/
virtual int32_t handleComputeJulianDay(UCalendarDateFields bestField) override;
virtual int32_t handleComputeJulianDay(UCalendarDateFields bestField, UErrorCode& status) override;

/**
* Return the number of days in the given month of the given extended
Expand Down
13 changes: 13 additions & 0 deletions icu4c/source/test/intltest/incaltst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ void IntlCalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &n
TESTCASE_AUTO(TestConsistencyIslamicUmalqura);
TESTCASE_AUTO(TestConsistencyPersian);
TESTCASE_AUTO(TestConsistencyJapanese);
TESTCASE_AUTO(TestIslamicUmalquraCalendarSlow);
TESTCASE_AUTO_END;
}

Expand Down Expand Up @@ -1122,6 +1123,18 @@ void IntlCalendarTest::checkConsistency(const char* locale) {
}
}

void IntlCalendarTest::TestIslamicUmalquraCalendarSlow() {
IcuTestErrorCode status(*this, "TestIslamicUmalquraCalendarSlow");
Locale l("th@calendar=islamic-umalqura");
std::unique_ptr<Calendar> cal(
Calendar::createInstance(l, status));
cal->add(UCAL_YEAR, 1229080905, status);
cal->roll(UCAL_WEEK_OF_MONTH, 1499050699, status);
cal->fieldDifference(0.000000, UCAL_YEAR_WOY, status);
// Ignore the error
status.reset();
}

void IntlCalendarTest::simpleTest(const Locale& loc, const UnicodeString& expect, UDate expectDate, UErrorCode& status)
{
UnicodeString tmp;
Expand Down
1 change: 1 addition & 0 deletions icu4c/source/test/intltest/incaltst.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class IntlCalendarTest: public CalendarTimeZoneTest {
void TestConsistencyIslamicUmalqura();
void TestConsistencyPersian();
void TestConsistencyJapanese();
void TestIslamicUmalquraCalendarSlow();

protected:
// Test a Gregorian-Like calendar
Expand Down
4 changes: 4 additions & 0 deletions icu4j/main/core/src/main/java/com/ibm/icu/util/Calendar.java
Original file line number Diff line number Diff line change
Expand Up @@ -6233,6 +6233,10 @@ protected int handleComputeJulianDay(int bestField) {

internalSet(EXTENDED_YEAR, year);

if (year > Long.MAX_VALUE / 400) {
throw new IllegalArgumentException("year is too large");
}

int month = useMonth ? internalGetMonth(getDefaultMonthInYear(year)) : 0;

// Get the Julian day of the day BEFORE the start of this year.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2710,5 +2710,17 @@ public void TestRespectUExtensionFw() { // ICU-22226
Calendar.getInstance(Locale.forLanguageTag(localeIds[i])).getFirstDayOfWeek());
}
}

@Test
public void TestIslamicUmalquraCalendarSlow() { // ICU-22513
Locale loc = new Locale("th@calendar=islamic-umalqura");
Calendar cal = Calendar.getInstance(loc);
cal.clear();
cal.add(Calendar.YEAR, 1229080905);
cal.roll(Calendar.WEEK_OF_MONTH, 1499050699);
cal.fieldDifference(new Date(0), Calendar.YEAR_WOY);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but shouldn't there be some kind of "expect exception to be thrown" logic in this test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Java use long (64 bits) for those value so it will not throw exception with that setting. The test just make sure it will not be infinity loop or time out.


}

}
//eof