diff --git a/icu4c/source/i18n/calendar.cpp b/icu4c/source/i18n/calendar.cpp index 659c91b86a64..8408cbfee88c 100644 --- a/icu4c/source/i18n/calendar.cpp +++ b/icu4c/source/i18n/calendar.cpp @@ -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); @@ -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 @@ -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); @@ -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); diff --git a/icu4c/source/i18n/gregocal.cpp b/icu4c/source/i18n/gregocal.cpp index ec14abbbf10c..201b3a4d88fa 100644 --- a/icu4c/source/i18n/gregocal.cpp +++ b/icu4c/source/i18n/gregocal.cpp @@ -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); } @@ -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"); diff --git a/icu4c/source/i18n/unicode/calendar.h b/icu4c/source/i18n/unicode/calendar.h index 31851d666b34..c0364e68f31a 100644 --- a/icu4c/source/i18n/unicode/calendar.h +++ b/icu4c/source/i18n/unicode/calendar.h @@ -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 @@ -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 diff --git a/icu4c/source/i18n/unicode/gregocal.h b/icu4c/source/i18n/unicode/gregocal.h index ab5a9c15716b..20072303a676 100644 --- a/icu4c/source/i18n/unicode/gregocal.h +++ b/icu4c/source/i18n/unicode/gregocal.h @@ -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 diff --git a/icu4c/source/test/intltest/incaltst.cpp b/icu4c/source/test/intltest/incaltst.cpp index 4be0af09ed2a..5a682c3156ec 100644 --- a/icu4c/source/test/intltest/incaltst.cpp +++ b/icu4c/source/test/intltest/incaltst.cpp @@ -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; } @@ -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 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; diff --git a/icu4c/source/test/intltest/incaltst.h b/icu4c/source/test/intltest/incaltst.h index a29f7b316bff..f01d6b16c17d 100644 --- a/icu4c/source/test/intltest/incaltst.h +++ b/icu4c/source/test/intltest/incaltst.h @@ -60,6 +60,7 @@ class IntlCalendarTest: public CalendarTimeZoneTest { void TestConsistencyIslamicUmalqura(); void TestConsistencyPersian(); void TestConsistencyJapanese(); + void TestIslamicUmalquraCalendarSlow(); protected: // Test a Gregorian-Like calendar diff --git a/icu4j/main/core/src/main/java/com/ibm/icu/util/Calendar.java b/icu4j/main/core/src/main/java/com/ibm/icu/util/Calendar.java index 8459be6060b1..e915af13d6ce 100644 --- a/icu4j/main/core/src/main/java/com/ibm/icu/util/Calendar.java +++ b/icu4j/main/core/src/main/java/com/ibm/icu/util/Calendar.java @@ -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. diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java index 2d0e07225abd..7c13f3917f9f 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java @@ -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); + + } + } //eof