mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-06 22:15:31 +00:00
ICU-22633 Add more Calendar overflow tests
Test set+set, set+add, set+roll, add+add, roll+roll Fix more int32_t overflow problems. Optimize both Java and C++ Hebrew Calendar month/year advancement by first consider every 235 months is 19 years before iteration.
This commit is contained in:
parent
2a1853c9a9
commit
9a912bb51b
11 changed files with 272 additions and 70 deletions
|
@ -1532,7 +1532,7 @@ uint8_t Calendar::julianDayToDayOfWeek(int32_t julian)
|
|||
{
|
||||
// If julian is negative, then julian%7 will be negative, so we adjust
|
||||
// accordingly. We add 1 because Julian day 0 is Monday.
|
||||
int8_t dayOfWeek = (int8_t) ((julian + 1) % 7);
|
||||
int8_t dayOfWeek = (int8_t) ((julian + 1LL) % 7);
|
||||
|
||||
uint8_t result = (uint8_t)(dayOfWeek + ((dayOfWeek < 0) ? (7+UCAL_SUNDAY ) : UCAL_SUNDAY));
|
||||
return result;
|
||||
|
@ -1764,8 +1764,8 @@ void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& statu
|
|||
int32_t max = getActualMaximum(field,status);
|
||||
int32_t gap = max - min + 1;
|
||||
|
||||
int32_t value = internalGet(field) + amount;
|
||||
value = (value - min) % gap;
|
||||
int64_t value = internalGet(field);
|
||||
value = (value + amount - min) % gap;
|
||||
if (value < 0) {
|
||||
value += gap;
|
||||
}
|
||||
|
@ -1787,7 +1787,7 @@ void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& statu
|
|||
{
|
||||
// Assume min == 0 in calculations below
|
||||
double start = getTimeInMillis(status);
|
||||
int32_t oldHour = internalGet(field);
|
||||
int64_t oldHour = internalGet(field);
|
||||
int32_t max = getMaximum(field);
|
||||
int32_t newHour = (oldHour + amount) % (max + 1);
|
||||
if (newHour < 0) {
|
||||
|
@ -1804,11 +1804,12 @@ void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& statu
|
|||
// DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
|
||||
// E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
|
||||
{
|
||||
int32_t max = getActualMaximum(UCAL_MONTH, status);
|
||||
int32_t mon = (internalGet(UCAL_MONTH) + amount) % (max+1);
|
||||
int32_t max = getActualMaximum(UCAL_MONTH, status) + 1;
|
||||
int64_t mon = internalGet(UCAL_MONTH);
|
||||
mon = (mon + amount) % max;
|
||||
|
||||
if (mon < 0) {
|
||||
mon += (max + 1);
|
||||
mon += max;
|
||||
}
|
||||
set(UCAL_MONTH, mon);
|
||||
|
||||
|
@ -1830,11 +1831,19 @@ void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& statu
|
|||
if (era == 0) {
|
||||
const char * calType = getType();
|
||||
if ( uprv_strcmp(calType,"gregorian")==0 || uprv_strcmp(calType,"roc")==0 || uprv_strcmp(calType,"coptic")==0 ) {
|
||||
amount = -amount;
|
||||
if (uprv_mul32_overflow(amount, -1, &amount)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
era0WithYearsThatGoBackwards = true;
|
||||
}
|
||||
}
|
||||
int32_t newYear = internalGet(field) + amount;
|
||||
int32_t newYear;
|
||||
if (uprv_add32_overflow(
|
||||
amount, internalGet(field), &newYear)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
if (era > 0 || newYear >= 1) {
|
||||
int32_t maxYear = getActualMaximum(field, status);
|
||||
if (maxYear < 32768) {
|
||||
|
@ -1862,7 +1871,12 @@ void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& statu
|
|||
|
||||
case UCAL_EXTENDED_YEAR:
|
||||
// Rolling the year can involve pinning the DAY_OF_MONTH.
|
||||
set(field, internalGet(field) + amount);
|
||||
if (uprv_add32_overflow(
|
||||
amount, internalGet(field), &amount)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
set(field, amount);
|
||||
pinField(UCAL_MONTH,status);
|
||||
pinField(UCAL_DAY_OF_MONTH,status);
|
||||
return;
|
||||
|
@ -1934,7 +1948,7 @@ void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& statu
|
|||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return;
|
||||
}
|
||||
int32_t day_of_month = (internalGet(UCAL_DAY_OF_MONTH) + amount*7 -
|
||||
int32_t day_of_month = (internalGet(UCAL_DAY_OF_MONTH) + amount*7LL -
|
||||
start) % gap;
|
||||
if (day_of_month < 0) day_of_month += gap;
|
||||
day_of_month += start;
|
||||
|
@ -1996,7 +2010,7 @@ void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& statu
|
|||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return;
|
||||
}
|
||||
int32_t day_of_year = (internalGet(UCAL_DAY_OF_YEAR) + amount*7 -
|
||||
int32_t day_of_year = (internalGet(UCAL_DAY_OF_YEAR) + amount*7LL -
|
||||
start) % gap;
|
||||
if (day_of_year < 0) day_of_year += gap;
|
||||
day_of_year += start;
|
||||
|
|
|
@ -285,11 +285,11 @@ int32_t ChineseCalendar::handleGetMonthLength(int32_t extendedYear, int32_t mont
|
|||
* <p>Compute the ChineseCalendar-specific field IS_LEAP_MONTH.
|
||||
* @stable ICU 2.8
|
||||
*/
|
||||
void ChineseCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) {
|
||||
void ChineseCalendar::handleComputeFields(int32_t julianDay, UErrorCode & status) {
|
||||
|
||||
computeChineseFields(julianDay - kEpochStartAsJulianDay, // local days
|
||||
getGregorianYear(), getGregorianMonth(),
|
||||
true); // set all fields
|
||||
true, status); // set all fields
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -364,8 +364,11 @@ int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
|
|||
}
|
||||
int32_t theNewYear = newYear(gyear);
|
||||
int32_t newMoon = newMoonNear(theNewYear + month * 29, true);
|
||||
|
||||
int64_t julianDay = newMoon + kEpochStartAsJulianDay;
|
||||
int32_t julianDay;
|
||||
if (uprv_add32_overflow(newMoon, kEpochStartAsJulianDay, &julianDay)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Ignore IS_LEAP_MONTH field if useMonth is false
|
||||
int32_t isLeapMonth = useMonth ? internalGet(UCAL_IS_LEAP_MONTH) : 0;
|
||||
|
@ -383,15 +386,22 @@ int64_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
|
|||
|
||||
// This will modify the MONTH and IS_LEAP_MONTH fields (only)
|
||||
work->computeChineseFields(newMoon, work->getGregorianYear(),
|
||||
work->getGregorianMonth(), false);
|
||||
work->getGregorianMonth(), false, status);
|
||||
|
||||
if (month != work->internalGet(UCAL_MONTH) ||
|
||||
isLeapMonth != work->internalGet(UCAL_IS_LEAP_MONTH)) {
|
||||
newMoon = newMoonNear(newMoon + SYNODIC_GAP, true);
|
||||
julianDay = newMoon + kEpochStartAsJulianDay;
|
||||
if (uprv_add32_overflow(newMoon, kEpochStartAsJulianDay, &julianDay)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (uprv_add32_overflow(julianDay, -1, &julianDay)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return julianDay - 1;
|
||||
return julianDay;
|
||||
}
|
||||
|
||||
|
||||
|
@ -714,7 +724,10 @@ UBool ChineseCalendar::isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) co
|
|||
* and IS_LEAP_MONTH fields.
|
||||
*/
|
||||
void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t gmonth,
|
||||
UBool setAllFields) {
|
||||
UBool setAllFields, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
// 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.
|
||||
|
@ -774,6 +787,22 @@ void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t
|
|||
}
|
||||
int32_t dayOfMonth = days - thisMoon + 1;
|
||||
|
||||
int32_t min_eyear = handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MINIMUM);
|
||||
if (extended_year < min_eyear) {
|
||||
if (!isLenient()) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
extended_year = min_eyear;
|
||||
}
|
||||
int32_t max_eyear = handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MAXIMUM);
|
||||
if (max_eyear < extended_year) {
|
||||
if (!isLenient()) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
extended_year = max_eyear;
|
||||
}
|
||||
internalSet(UCAL_EXTENDED_YEAR, extended_year);
|
||||
|
||||
// 0->0,60 1->1,1 60->1,60 61->2,1 etc.
|
||||
|
@ -893,7 +922,11 @@ int32_t ChineseCalendar::getRelatedYear(UErrorCode &status) const
|
|||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
return year + kChineseRelatedYearDiff;
|
||||
if (uprv_add32_overflow(year, kChineseRelatedYearDiff, &year)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
return year;
|
||||
}
|
||||
|
||||
void ChineseCalendar::setRelatedYear(int32_t year)
|
||||
|
|
|
@ -250,7 +250,7 @@ class U_I18N_API ChineseCalendar : public Calendar {
|
|||
virtual UBool hasNoMajorSolarTerm(int32_t newMoon) const;
|
||||
virtual UBool isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) const;
|
||||
virtual void computeChineseFields(int32_t days, int32_t gyear,
|
||||
int32_t gmonth, UBool setAllFields);
|
||||
int32_t gmonth, UBool setAllFields, UErrorCode& status);
|
||||
virtual int32_t newYear(int32_t gyear) const;
|
||||
virtual void offsetMonth(int32_t newMoon, int32_t dom, int32_t delta, UErrorCode& status);
|
||||
const TimeZone* getChineseCalZoneAstroCalc() const;
|
||||
|
|
|
@ -151,7 +151,11 @@ int32_t DangiCalendar::getRelatedYear(UErrorCode &status) const
|
|||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
return year + kDangiRelatedYearDiff;
|
||||
if (uprv_add32_overflow(year, kDangiRelatedYearDiff, &year)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
return year;
|
||||
}
|
||||
|
||||
void DangiCalendar::setRelatedYear(int32_t year)
|
||||
|
|
|
@ -879,7 +879,10 @@ GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& s
|
|||
isoDoy -= handleGetYearLength(isoYear - 1);
|
||||
}
|
||||
}
|
||||
woy += amount;
|
||||
if (uprv_add32_overflow(woy, amount, &woy)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
// Do fast checks to avoid unnecessary computation:
|
||||
if (woy < 1 || woy > 52) {
|
||||
// Determine the last week of the ISO year.
|
||||
|
|
|
@ -136,6 +136,10 @@ static const int16_t LEAP_MONTH_START[][3] = {
|
|||
{ 383, 384, 385 }, // Elul
|
||||
};
|
||||
|
||||
// There are 235 months in 19 years cycle.
|
||||
static const int32_t MONTHS_IN_CYCLE = 235;
|
||||
static const int32_t YEARS_IN_CYCLE = 19;
|
||||
|
||||
static icu::CalendarCache *gCache = nullptr;
|
||||
|
||||
U_CDECL_BEGIN
|
||||
|
@ -227,12 +231,23 @@ void HebrewCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode&
|
|||
// ADAR_1 -- then we have to bump to ADAR_2 aka ADAR. But
|
||||
// if amount is -2 and we land in ADAR_1, then we have to
|
||||
// bump the other way -- down to SHEVAT. - Alan 11/00
|
||||
int32_t month = get(UCAL_MONTH, status);
|
||||
int64_t month = get(UCAL_MONTH, status);
|
||||
int32_t year = get(UCAL_YEAR, status);
|
||||
UBool acrossAdar1;
|
||||
if (amount > 0) {
|
||||
acrossAdar1 = (month < ADAR_1); // started before ADAR_1?
|
||||
month += amount;
|
||||
// We know there are total 235 months in every 19 years. To speed
|
||||
// up the iteration, we first fast forward in the multiple of 235
|
||||
// months for 19 years before the iteration which check the leap year.
|
||||
if (month >= MONTHS_IN_CYCLE) {
|
||||
if (uprv_add32_overflow(year, (month / MONTHS_IN_CYCLE) * YEARS_IN_CYCLE, &year)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
month %= MONTHS_IN_CYCLE;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (acrossAdar1 && month>=ADAR_1 && !isLeapYear(year)) {
|
||||
++month;
|
||||
|
@ -247,6 +262,16 @@ void HebrewCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode&
|
|||
} else {
|
||||
acrossAdar1 = (month > ADAR_1); // started after ADAR_1?
|
||||
month += amount;
|
||||
// We know there are total 235 months in every 19 years. To speed
|
||||
// up the iteration, we first fast forward in the multiple of 235
|
||||
// months for 19 years before the iteration which check the leap year.
|
||||
if (month <= -MONTHS_IN_CYCLE) {
|
||||
if (uprv_add32_overflow(year, (month / MONTHS_IN_CYCLE) * YEARS_IN_CYCLE, &year)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
month %= MONTHS_IN_CYCLE;
|
||||
}
|
||||
for (;;) {
|
||||
if (acrossAdar1 && month<=ADAR_1 && !isLeapYear(year)) {
|
||||
--month;
|
||||
|
@ -485,7 +510,7 @@ int32_t HebrewCalendar::yearType(int32_t year) const
|
|||
*/
|
||||
UBool HebrewCalendar::isLeapYear(int32_t year) {
|
||||
//return (year * 12 + 17) % 19 >= 12;
|
||||
int64_t x = (year*12LL + 17) % 19;
|
||||
int64_t x = (year*12LL + 17) % YEARS_IN_CYCLE;
|
||||
return x >= ((x < 0) ? -7 : 12);
|
||||
}
|
||||
|
||||
|
@ -620,6 +645,23 @@ void HebrewCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status)
|
|||
int dayOfMonth = dayOfYear - (isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type]);
|
||||
|
||||
internalSet(UCAL_ERA, 0);
|
||||
// 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
internalSet(UCAL_YEAR, year);
|
||||
internalSet(UCAL_EXTENDED_YEAR, year);
|
||||
int32_t ordinal_month = month;
|
||||
|
@ -667,6 +709,16 @@ int64_t HebrewCalendar::handleComputeMonthStart(
|
|||
// on the year) but since we _always_ number from 0..12, and
|
||||
// the leap year determines whether or not month 5 (Adar 1)
|
||||
// is present, we allow 0..12 in any given year.
|
||||
|
||||
// The month could be in large value, we first roll 235 months to 19 years
|
||||
// before the while loop.
|
||||
if (month <= -MONTHS_IN_CYCLE || month >= MONTHS_IN_CYCLE) {
|
||||
if (uprv_add32_overflow(eyear, (month / MONTHS_IN_CYCLE) * YEARS_IN_CYCLE, &eyear)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
month %= MONTHS_IN_CYCLE;
|
||||
}
|
||||
while (month < 0) {
|
||||
if (uprv_add32_overflow(eyear, -1, &eyear) ||
|
||||
uprv_add32_overflow(month, monthsInYear(eyear), &month)) {
|
||||
|
|
|
@ -316,7 +316,11 @@ int32_t IndianCalendar::getRelatedYear(UErrorCode &status) const
|
|||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
return year + kIndianRelatedYearDiff;
|
||||
if (uprv_add32_overflow(year, kIndianRelatedYearDiff, &year)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
return year;
|
||||
}
|
||||
|
||||
void IndianCalendar::setRelatedYear(int32_t year)
|
||||
|
|
|
@ -265,7 +265,11 @@ int32_t PersianCalendar::getRelatedYear(UErrorCode &status) const
|
|||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
}
|
||||
return year + kPersianRelatedYearDiff;
|
||||
if (uprv_add32_overflow(year, kPersianRelatedYearDiff, &year)) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
return year;
|
||||
}
|
||||
|
||||
void PersianCalendar::setRelatedYear(int32_t year)
|
||||
|
|
|
@ -198,6 +198,11 @@ void CalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &name,
|
|||
TESTCASE_AUTO(Test22633HebrewOverflow);
|
||||
TESTCASE_AUTO(Test22633AMPMOverflow);
|
||||
TESTCASE_AUTO(Test22633SetGetTimeOverflow);
|
||||
TESTCASE_AUTO(Test22633Set2FieldsGetTimeOverflow);
|
||||
TESTCASE_AUTO(Test22633SetAddGetTimeOverflow);
|
||||
TESTCASE_AUTO(Test22633SetRollGetTimeOverflow);
|
||||
TESTCASE_AUTO(Test22633AddTwiceGetTimeOverflow);
|
||||
TESTCASE_AUTO(Test22633RollTwiceGetTimeOverflow);
|
||||
|
||||
TESTCASE_AUTO(TestAddOverflow);
|
||||
|
||||
|
@ -5720,56 +5725,123 @@ void CalendarTest::Test22633AMPMOverflow() {
|
|||
assertTrue("Should return success", U_SUCCESS(status));
|
||||
}
|
||||
|
||||
void CalendarTest::Test22633SetGetTimeOverflow() {
|
||||
void CalendarTest::RunTestOnCalendars(void(TestFunc)(Calendar*, UCalendarDateFields)) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
Locale locale = Locale::getEnglish();
|
||||
LocalPointer<StringEnumeration> values(
|
||||
Calendar::getKeywordValuesForLocale("calendar", locale, false, status),
|
||||
status);
|
||||
assertTrue("Should return success", U_SUCCESS(status));
|
||||
if (U_SUCCESS(status)) {
|
||||
const char* value = nullptr;
|
||||
while ((value = values->next(nullptr, status)) != nullptr && U_SUCCESS(status)) {
|
||||
bool isHebrew = strcmp("hebrew", value) == 0;
|
||||
locale.setKeywordValue("calendar", value, status);
|
||||
assertTrue("Should return success", U_SUCCESS(status));
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
const char* value = nullptr;
|
||||
while ((value = values->next(nullptr, status)) != nullptr && U_SUCCESS(status)) {
|
||||
locale.setKeywordValue("calendar", value, status);
|
||||
assertTrue("Should return success", U_SUCCESS(status));
|
||||
|
||||
LocalPointer<Calendar> cal(Calendar::createInstance(*TimeZone::getGMT(), locale, status), status);
|
||||
assertTrue("Should return success", U_SUCCESS(status));
|
||||
|
||||
assertTrue("Should return success", U_SUCCESS(status));
|
||||
for (int32_t i = 0; i < UCAL_FIELD_COUNT; i++) {
|
||||
status = U_ZERO_ERROR;
|
||||
cal->clear();
|
||||
cal->set(static_cast<UCalendarDateFields>(i), INT32_MAX);
|
||||
cal->getTime(status);
|
||||
|
||||
status = U_ZERO_ERROR;
|
||||
cal->clear();
|
||||
cal->set(static_cast<UCalendarDateFields>(i), INT32_MIN);
|
||||
cal->getTime(status);
|
||||
|
||||
if (!isHebrew) {
|
||||
for (int32_t j = 0; j < UCAL_FIELD_COUNT; j++) {
|
||||
status = U_ZERO_ERROR;
|
||||
cal->clear();
|
||||
cal->set(static_cast<UCalendarDateFields>(i), INT32_MAX);
|
||||
cal->set(static_cast<UCalendarDateFields>(j), INT32_MAX);
|
||||
cal->getTime(status);
|
||||
|
||||
status = U_ZERO_ERROR;
|
||||
cal->clear();
|
||||
cal->set(static_cast<UCalendarDateFields>(i), INT32_MIN);
|
||||
cal->set(static_cast<UCalendarDateFields>(j), INT32_MIN);
|
||||
cal->getTime(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
status = U_ZERO_ERROR;
|
||||
LocalPointer<Calendar> cal(Calendar::createInstance(*TimeZone::getGMT(), locale, status), status);
|
||||
assertTrue("Should return success", U_SUCCESS(status));
|
||||
for (int32_t i = 0; i < UCAL_FIELD_COUNT; i++) {
|
||||
TestFunc(cal.getAlias(), static_cast<UCalendarDateFields>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This test is designed to work with undefined behavior sanitizer UBSAN to
|
||||
// ensure we do not have math operation overflow int32_t.
|
||||
void CalendarTest::Test22633SetGetTimeOverflow() {
|
||||
RunTestOnCalendars([](Calendar* cal, UCalendarDateFields field) {
|
||||
auto f = [](Calendar* cal, UCalendarDateFields field, int32_t value) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
cal->clear();
|
||||
cal->set(field, value);
|
||||
cal->getTime(status);
|
||||
};
|
||||
f(cal, field, INT32_MAX);
|
||||
f(cal, field, INT32_MIN);
|
||||
});
|
||||
}
|
||||
|
||||
void CalendarTest::Test22633Set2FieldsGetTimeOverflow() {
|
||||
RunTestOnCalendars([](Calendar* cal, UCalendarDateFields field) {
|
||||
auto f = [](Calendar* cal, UCalendarDateFields field, int32_t value) {
|
||||
for (int32_t j = 0; j < UCAL_FIELD_COUNT; j++) {
|
||||
UCalendarDateFields field2 = static_cast<UCalendarDateFields>(j);
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
cal->clear();
|
||||
cal->set(field, value);
|
||||
cal->set(field2, value);
|
||||
cal->getTime(status);
|
||||
}
|
||||
};
|
||||
f(cal, field, INT32_MAX);
|
||||
f(cal, field, INT32_MIN);
|
||||
});
|
||||
}
|
||||
|
||||
void CalendarTest::Test22633SetAddGetTimeOverflow() {
|
||||
RunTestOnCalendars([](Calendar* cal, UCalendarDateFields field) {
|
||||
auto f = [](Calendar* cal, UCalendarDateFields field, int32_t value) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
cal->clear();
|
||||
cal->set(field, value);
|
||||
cal->add(field, value, status);
|
||||
status = U_ZERO_ERROR;
|
||||
cal->getTime(status);
|
||||
};
|
||||
f(cal, field, INT32_MAX);
|
||||
f(cal, field, INT32_MIN);
|
||||
});
|
||||
}
|
||||
|
||||
void CalendarTest::Test22633AddTwiceGetTimeOverflow() {
|
||||
RunTestOnCalendars([](Calendar* cal, UCalendarDateFields field) {
|
||||
auto f = [](Calendar* cal, UCalendarDateFields field, int32_t value) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
cal->clear();
|
||||
cal->add(field, value, status);
|
||||
status = U_ZERO_ERROR;
|
||||
cal->add(field, value, status);
|
||||
status = U_ZERO_ERROR;
|
||||
cal->getTime(status);
|
||||
};
|
||||
f(cal, field, INT32_MAX);
|
||||
f(cal, field, INT32_MIN);
|
||||
});
|
||||
}
|
||||
|
||||
void CalendarTest::Test22633SetRollGetTimeOverflow() {
|
||||
RunTestOnCalendars([](Calendar* cal, UCalendarDateFields field) {
|
||||
auto f = [](Calendar* cal, UCalendarDateFields field, int32_t value) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
cal->clear();
|
||||
cal->set(field, value);
|
||||
cal->roll(field, value, status);
|
||||
status = U_ZERO_ERROR;
|
||||
cal->getTime(status);
|
||||
};
|
||||
f(cal, field, INT32_MAX);
|
||||
f(cal, field, INT32_MIN);
|
||||
});
|
||||
}
|
||||
|
||||
void CalendarTest::Test22633RollTwiceGetTimeOverflow() {
|
||||
RunTestOnCalendars([](Calendar* cal, UCalendarDateFields field) {
|
||||
auto f = [](Calendar* cal, UCalendarDateFields field, int32_t value) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
cal->clear();
|
||||
cal->roll(field, value, status);
|
||||
status = U_ZERO_ERROR;
|
||||
cal->roll(field, value, status);
|
||||
status = U_ZERO_ERROR;
|
||||
cal->getTime(status);
|
||||
};
|
||||
f(cal, field, INT32_MAX);
|
||||
f(cal, field, INT32_MIN);
|
||||
});
|
||||
}
|
||||
|
||||
void CalendarTest::TestChineseCalendarComputeMonthStart() { // ICU-22639
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
|
|
|
@ -341,6 +341,12 @@ public: // package
|
|||
void Test22633HebrewOverflow();
|
||||
void Test22633AMPMOverflow();
|
||||
void Test22633SetGetTimeOverflow();
|
||||
void Test22633Set2FieldsGetTimeOverflow();
|
||||
void Test22633SetAddGetTimeOverflow();
|
||||
void Test22633SetRollGetTimeOverflow();
|
||||
void Test22633AddTwiceGetTimeOverflow();
|
||||
void Test22633RollTwiceGetTimeOverflow();
|
||||
void RunTestOnCalendars(void(TestFunc)(Calendar*, UCalendarDateFields));
|
||||
|
||||
void verifyFirstDayOfWeek(const char* locale, UCalendarDaysOfWeek expected);
|
||||
void TestFirstDayOfWeek();
|
||||
|
|
|
@ -267,6 +267,8 @@ public class HebrewCalendar extends Calendar {
|
|||
{ 383, 384, 385 }, // Elul
|
||||
};
|
||||
|
||||
private static final int MONTHS_IN_CYCLE = 235;
|
||||
private static final int YEARS_IN_CYCLE = 19;
|
||||
//-------------------------------------------------------------------------
|
||||
// Data Members...
|
||||
//-------------------------------------------------------------------------
|
||||
|
@ -615,7 +617,7 @@ public class HebrewCalendar extends Calendar {
|
|||
|
||||
if (day == CalendarCache.EMPTY) {
|
||||
// # of months before year
|
||||
int months = (int)floorDivide((235 * (long)year - 234), 19);
|
||||
int months = (int)floorDivide((MONTHS_IN_CYCLE * (long)year - (MONTHS_IN_CYCLE-1)), YEARS_IN_CYCLE);
|
||||
|
||||
long frac = months * MONTH_FRACT + BAHARAD; // Fractional part of day #
|
||||
day = months * 29 + (frac / DAY_PARTS); // Whole # part of calculation
|
||||
|
@ -698,7 +700,7 @@ public class HebrewCalendar extends Calendar {
|
|||
@Deprecated
|
||||
public static boolean isLeapYear(int year) {
|
||||
//return (year * 12 + 17) % 19 >= 12;
|
||||
int x = (year*12 + 17) % 19;
|
||||
int x = (year*12 + 17) % YEARS_IN_CYCLE;
|
||||
return x >= ((x < 0) ? -7 : 12);
|
||||
}
|
||||
|
||||
|
@ -730,6 +732,10 @@ public class HebrewCalendar extends Calendar {
|
|||
// on the year) but since we _always_ number from 0..12, and
|
||||
// the leap year determines whether or not month 5 (Adar 1)
|
||||
// is present, we allow 0..12 in any given year.
|
||||
if (month <= -MONTHS_IN_CYCLE || MONTHS_IN_CYCLE <= month) {
|
||||
extendedYear += (month / MONTHS_IN_CYCLE) * YEARS_IN_CYCLE;
|
||||
month = month % MONTHS_IN_CYCLE;
|
||||
}
|
||||
while (month < 0) {
|
||||
month += monthsInYear(--extendedYear);
|
||||
}
|
||||
|
@ -807,7 +813,7 @@ public class HebrewCalendar extends Calendar {
|
|||
protected void handleComputeFields(int julianDay) {
|
||||
long d = julianDay - 347997;
|
||||
long m = floorDivide((d * DAY_PARTS), MONTH_PARTS); // Months (approx)
|
||||
int year = (int)(floorDivide((19 * m + 234), 235) + 1); // Years (approx)
|
||||
int year = (int)(floorDivide((YEARS_IN_CYCLE * m + (MONTHS_IN_CYCLE-1)), MONTHS_IN_CYCLE) + 1); // Years (approx)
|
||||
long ys = startOfYear(year); // 1st day of year
|
||||
int dayOfYear = (int)(d - ys);
|
||||
|
||||
|
@ -874,6 +880,10 @@ public class HebrewCalendar extends Calendar {
|
|||
// on the year) but since we _always_ number from 0..12, and
|
||||
// the leap year determines whether or not month 5 (Adar 1)
|
||||
// is present, we allow 0..12 in any given year.
|
||||
if (month <= -MONTHS_IN_CYCLE || MONTHS_IN_CYCLE <= month) {
|
||||
eyear += (month / MONTHS_IN_CYCLE) * YEARS_IN_CYCLE;
|
||||
month = month % MONTHS_IN_CYCLE;
|
||||
}
|
||||
while (month < 0) {
|
||||
month += monthsInYear(--eyear);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue