ICU-12504 in ICU4C Persian cal, use int64_t math for one operation to avoid overflow; add tests in C and J
X-SVN-Rev: 40654
This commit is contained in:
parent
4b67416113
commit
71dd84d4ff
@ -27,6 +27,11 @@ int32_t ClockMath::floorDivide(int32_t numerator, int32_t denominator) {
|
||||
numerator / denominator : ((numerator + 1) / denominator) - 1;
|
||||
}
|
||||
|
||||
int64_t ClockMath::floorDivide(int64_t numerator, int64_t denominator) {
|
||||
return (numerator >= 0) ?
|
||||
numerator / denominator : ((numerator + 1) / denominator) - 1;
|
||||
}
|
||||
|
||||
int32_t ClockMath::floorDivide(double numerator, int32_t denominator,
|
||||
int32_t& remainder) {
|
||||
double quotient;
|
||||
|
@ -40,6 +40,17 @@ class ClockMath {
|
||||
*/
|
||||
static int32_t floorDivide(int32_t numerator, int32_t denominator);
|
||||
|
||||
/**
|
||||
* Divide two integers, returning the floor of the quotient.
|
||||
* Unlike the built-in division, this is mathematically
|
||||
* well-behaved. E.g., <code>-1/4</code> => 0 but
|
||||
* <code>floorDivide(-1,4)</code> => -1.
|
||||
* @param numerator the numerator
|
||||
* @param denominator a divisor which must be != 0
|
||||
* @return the floor of the quotient
|
||||
*/
|
||||
static int64_t floorDivide(int64_t numerator, int64_t denominator);
|
||||
|
||||
/**
|
||||
* Divide two numbers, returning the floor of the quotient.
|
||||
* Unlike the built-in division, this is mathematically
|
||||
|
@ -213,7 +213,7 @@ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*statu
|
||||
int32_t year, month, dayOfMonth, dayOfYear;
|
||||
|
||||
int32_t daysSinceEpoch = julianDay - PERSIAN_EPOCH;
|
||||
year = 1 + ClockMath::floorDivide(33 * daysSinceEpoch + 3, 12053);
|
||||
year = 1 + (int32_t)ClockMath::floorDivide(33 * (int64_t)daysSinceEpoch + 3, (int64_t)12053);
|
||||
|
||||
int32_t farvardin1 = 365 * (year - 1) + ClockMath::floorDivide(8 * year + 21, 33);
|
||||
dayOfYear = (daysSinceEpoch - farvardin1); // 0-based
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "calregts.h"
|
||||
|
||||
#include "unicode/calendar.h"
|
||||
#include "unicode/gregocal.h"
|
||||
#include "unicode/simpletz.h"
|
||||
#include "unicode/smpdtfmt.h"
|
||||
@ -91,6 +92,7 @@ CalendarRegressionTest::runIndexedTest( int32_t index, UBool exec, const char* &
|
||||
CASE(49,Test9019);
|
||||
CASE(50,TestT9452);
|
||||
CASE(51,TestT11632);
|
||||
CASE(52,TestPersianCalOverflow);
|
||||
default: name = ""; break;
|
||||
}
|
||||
}
|
||||
@ -2987,4 +2989,34 @@ void CalendarRegressionTest::TestT11632(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @bug ticket 13454
|
||||
*/
|
||||
void CalendarRegressionTest::TestPersianCalOverflow(void) {
|
||||
const char* localeID = "bs_Cyrl@calendar=persian";
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
Calendar* cal = Calendar::createInstance(Locale(localeID), status);
|
||||
if(U_FAILURE(status)) {
|
||||
dataerrln("FAIL: Calendar::createInstance for localeID %s: %s", localeID, u_errorName(status));
|
||||
} else {
|
||||
int32_t maxMonth = cal->getMaximum(UCAL_MONTH);
|
||||
int32_t maxDayOfMonth = cal->getMaximum(UCAL_DATE);
|
||||
int32_t jd, month, dayOfMonth;
|
||||
for (jd = 67023580; jd <= 67023584; jd++) { // year 178171, int32_t overflow if jd >= 67023582
|
||||
status = U_ZERO_ERROR;
|
||||
cal->clear();
|
||||
cal->set(UCAL_JULIAN_DAY, jd);
|
||||
month = cal->get(UCAL_MONTH, status);
|
||||
dayOfMonth = cal->get(UCAL_DATE, status);
|
||||
if ( U_FAILURE(status) ) {
|
||||
errln("FAIL: Calendar->get MONTH/DATE for localeID %s, julianDay %d, status %s\n", localeID, jd, u_errorName(status));
|
||||
} else if (month > maxMonth || dayOfMonth > maxDayOfMonth) {
|
||||
errln("FAIL: localeID %s, julianDay %d; maxMonth %d, got month %d; maxDayOfMonth %d, got dayOfMonth %d\n",
|
||||
localeID, jd, maxMonth, month, maxDayOfMonth, dayOfMonth);
|
||||
}
|
||||
}
|
||||
delete cal;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
@ -78,6 +78,7 @@ public:
|
||||
void Test9019(void);
|
||||
void TestT9452(void);
|
||||
void TestT11632(void);
|
||||
void TestPersianCalOverflow(void);
|
||||
|
||||
void printdate(GregorianCalendar *cal, const char *string);
|
||||
void dowTest(UBool lenient) ;
|
||||
|
@ -2503,5 +2503,23 @@ public class CalendarRegressionTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
assertEquals("Incorrect time for long range milliseconds","Sun Jan 25 21:00:00 PST 1970", cal.getTime().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestPersianCalOverflow() {
|
||||
String localeID = "bs_Cyrl@calendar=persian";
|
||||
Calendar cal = Calendar.getInstance(new ULocale(localeID));
|
||||
int maxMonth = cal.getMaximum(Calendar.MONTH);
|
||||
int maxDayOfMonth = cal.getMaximum(Calendar.DATE);
|
||||
int jd, month, dayOfMonth;
|
||||
for (jd = 67023580; jd <= 67023584; jd++) { // year 178171, int32_t overflow if jd >= 67023582
|
||||
cal.clear();
|
||||
cal.set(Calendar.JULIAN_DAY, jd);
|
||||
month = cal.get(Calendar.MONTH);
|
||||
dayOfMonth = cal.get(Calendar.DATE);
|
||||
if (month > maxMonth || dayOfMonth > maxDayOfMonth) {
|
||||
errln("Error: localeID " + localeID + ", julianDay " + jd + "; maxMonth " + maxMonth + ", got month " + month +
|
||||
"; maxDayOfMonth " + maxDayOfMonth + ", got dayOfMonth " + dayOfMonth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//eof
|
||||
|
Loading…
Reference in New Issue
Block a user