mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-10 07:39:16 +00:00
ICU-22023 Fix Calendar::get() return out of bound value and SimpleDateTime::format assert while TimeZone is "UTC" and value is -1e-9
See #2086
This commit is contained in:
parent
85705f04e0
commit
3d89af0f72
14 changed files with 202 additions and 42 deletions
|
@ -639,8 +639,8 @@ static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = {
|
|||
{ 0, 0, 59, 59 }, // MINUTE
|
||||
{ 0, 0, 59, 59 }, // SECOND
|
||||
{ 0, 0, 999, 999 }, // MILLISECOND
|
||||
{-12*kOneHour, -12*kOneHour, 12*kOneHour, 15*kOneHour }, // ZONE_OFFSET
|
||||
{ 0, 0, 1*kOneHour, 1*kOneHour }, // DST_OFFSET
|
||||
{-12*kOneHour, -12*kOneHour, 12*kOneHour, 30*kOneHour }, // ZONE_OFFSET
|
||||
{ -1*kOneHour, -1*kOneHour, 2*kOneHour, 2*kOneHour }, // DST_OFFSET
|
||||
{/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // YEAR_WOY
|
||||
{ 1, 1, 7, 7 }, // DOW_LOCAL
|
||||
{/*N/A*/-1, /*N/A*/-1, /*N/A*/-1, /*N/A*/-1}, // EXTENDED_YEAR
|
||||
|
@ -1537,7 +1537,8 @@ void Calendar::computeFields(UErrorCode &ec)
|
|||
// JULIAN_DAY field and also removes some inelegant code. - Liu
|
||||
// 11/6/00
|
||||
|
||||
int32_t days = (int32_t)ClockMath::floorDivide(localMillis, (double)kOneDay);
|
||||
int32_t millisInDay;
|
||||
int32_t days = ClockMath::floorDivide(localMillis, kOneDay, &millisInDay);
|
||||
|
||||
internalSet(UCAL_JULIAN_DAY,days + kEpochStartAsJulianDay);
|
||||
|
||||
|
@ -1561,19 +1562,47 @@ void Calendar::computeFields(UErrorCode &ec)
|
|||
// Compute time-related fields. These are independent of the date and
|
||||
// of the subclass algorithm. They depend only on the local zone
|
||||
// wall milliseconds in day.
|
||||
int32_t millisInDay = (int32_t) (localMillis - (days * kOneDay));
|
||||
|
||||
fFields[UCAL_MILLISECONDS_IN_DAY] = millisInDay;
|
||||
U_ASSERT(getMinimum(UCAL_MILLISECONDS_IN_DAY) <=
|
||||
fFields[UCAL_MILLISECONDS_IN_DAY]);
|
||||
U_ASSERT(fFields[UCAL_MILLISECONDS_IN_DAY] <=
|
||||
getMaximum(UCAL_MILLISECONDS_IN_DAY));
|
||||
|
||||
fFields[UCAL_MILLISECOND] = millisInDay % 1000;
|
||||
U_ASSERT(getMinimum(UCAL_MILLISECOND) <= fFields[UCAL_MILLISECOND]);
|
||||
U_ASSERT(fFields[UCAL_MILLISECOND] <= getMaximum(UCAL_MILLISECOND));
|
||||
|
||||
millisInDay /= 1000;
|
||||
fFields[UCAL_SECOND] = millisInDay % 60;
|
||||
U_ASSERT(getMinimum(UCAL_SECOND) <= fFields[UCAL_SECOND]);
|
||||
U_ASSERT(fFields[UCAL_SECOND] <= getMaximum(UCAL_SECOND));
|
||||
|
||||
millisInDay /= 60;
|
||||
fFields[UCAL_MINUTE] = millisInDay % 60;
|
||||
U_ASSERT(getMinimum(UCAL_MINUTE) <= fFields[UCAL_MINUTE]);
|
||||
U_ASSERT(fFields[UCAL_MINUTE] <= getMaximum(UCAL_MINUTE));
|
||||
|
||||
millisInDay /= 60;
|
||||
fFields[UCAL_HOUR_OF_DAY] = millisInDay;
|
||||
U_ASSERT(getMinimum(UCAL_HOUR_OF_DAY) <= fFields[UCAL_HOUR_OF_DAY]);
|
||||
U_ASSERT(fFields[UCAL_HOUR_OF_DAY] <= getMaximum(UCAL_HOUR_OF_DAY));
|
||||
|
||||
fFields[UCAL_AM_PM] = millisInDay / 12; // Assume AM == 0
|
||||
U_ASSERT(getMinimum(UCAL_AM_PM) <= fFields[UCAL_AM_PM]);
|
||||
U_ASSERT(fFields[UCAL_AM_PM] <= getMaximum(UCAL_AM_PM));
|
||||
|
||||
fFields[UCAL_HOUR] = millisInDay % 12;
|
||||
U_ASSERT(getMinimum(UCAL_HOUR) <= fFields[UCAL_HOUR]);
|
||||
U_ASSERT(fFields[UCAL_HOUR] <= getMaximum(UCAL_HOUR));
|
||||
|
||||
fFields[UCAL_ZONE_OFFSET] = rawOffset;
|
||||
U_ASSERT(getMinimum(UCAL_ZONE_OFFSET) <= fFields[UCAL_ZONE_OFFSET]);
|
||||
U_ASSERT(fFields[UCAL_ZONE_OFFSET] <= getMaximum(UCAL_ZONE_OFFSET));
|
||||
|
||||
fFields[UCAL_DST_OFFSET] = dstOffset;
|
||||
U_ASSERT(getMinimum(UCAL_DST_OFFSET) <= fFields[UCAL_DST_OFFSET]);
|
||||
U_ASSERT(fFields[UCAL_DST_OFFSET] <= getMaximum(UCAL_DST_OFFSET));
|
||||
}
|
||||
|
||||
uint8_t Calendar::julianDayToDayOfWeek(double julian)
|
||||
|
@ -1699,11 +1728,20 @@ void Calendar::computeWeekFields(UErrorCode &ec) {
|
|||
}
|
||||
fFields[UCAL_WEEK_OF_YEAR] = woy;
|
||||
fFields[UCAL_YEAR_WOY] = yearOfWeekOfYear;
|
||||
// min/max of years are not constrains for caller, so not assert here.
|
||||
// WEEK_OF_YEAR end
|
||||
|
||||
int32_t dayOfMonth = fFields[UCAL_DAY_OF_MONTH];
|
||||
fFields[UCAL_WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek);
|
||||
U_ASSERT(getMinimum(UCAL_WEEK_OF_MONTH) <= fFields[UCAL_WEEK_OF_MONTH]);
|
||||
U_ASSERT(fFields[UCAL_WEEK_OF_MONTH] <= getMaximum(UCAL_WEEK_OF_MONTH));
|
||||
|
||||
fFields[UCAL_DAY_OF_WEEK_IN_MONTH] = (dayOfMonth-1) / 7 + 1;
|
||||
U_ASSERT(getMinimum(UCAL_DAY_OF_WEEK_IN_MONTH) <=
|
||||
fFields[UCAL_DAY_OF_WEEK_IN_MONTH]);
|
||||
U_ASSERT(fFields[UCAL_DAY_OF_WEEK_IN_MONTH] <=
|
||||
getMaximum(UCAL_DAY_OF_WEEK_IN_MONTH));
|
||||
|
||||
#if defined (U_DEBUG_CAL)
|
||||
if(fFields[UCAL_DAY_OF_WEEK_IN_MONTH]==0) fprintf(stderr, "%s:%d: DOWIM %d on %g\n",
|
||||
__FILE__, __LINE__,fFields[UCAL_DAY_OF_WEEK_IN_MONTH], fTime);
|
||||
|
|
|
@ -135,7 +135,7 @@ CECalendar::jdToCE(int32_t julianDay, int32_t jdEpochOffset, int32_t& year, int3
|
|||
int32_t c4; // number of 4 year cycle (1461 days)
|
||||
int32_t r4; // remainder of 4 year cycle, always positive
|
||||
|
||||
c4 = ClockMath::floorDivide(julianDay - jdEpochOffset, 1461, r4);
|
||||
c4 = ClockMath::floorDivide(julianDay - jdEpochOffset, 1461, &r4);
|
||||
|
||||
year = 4 * c4 + (r4/365 - r4/1460); // 4 * <number of 4year cycle> + <years within the last cycle>
|
||||
|
||||
|
|
|
@ -328,7 +328,7 @@ int32_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
|
|||
// modify the extended year value accordingly.
|
||||
if (month < 0 || month > 11) {
|
||||
double m = month;
|
||||
eyear += (int32_t)ClockMath::floorDivide(m, 12.0, m);
|
||||
eyear += (int32_t)ClockMath::floorDivide(m, 12.0, &m);
|
||||
month = (int32_t)m;
|
||||
}
|
||||
|
||||
|
@ -727,7 +727,7 @@ void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t
|
|||
|
||||
// 0->0,60 1->1,1 60->1,60 61->2,1 etc.
|
||||
int32_t yearOfCycle;
|
||||
int32_t cycle = ClockMath::floorDivide(cycle_year - 1, 60, yearOfCycle);
|
||||
int32_t cycle = ClockMath::floorDivide(cycle_year - 1, 60, &yearOfCycle);
|
||||
internalSet(UCAL_ERA, cycle + 1);
|
||||
internalSet(UCAL_YEAR, yearOfCycle + 1);
|
||||
|
||||
|
|
|
@ -388,7 +388,7 @@ void GregorianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& statu
|
|||
// The Julian epoch day (not the same as Julian Day)
|
||||
// is zero on Saturday December 30, 0 (Gregorian).
|
||||
int32_t julianEpochDay = julianDay - (kJan1_1JulianDay - 2);
|
||||
eyear = (int32_t) ClockMath::floorDivide((4.0*julianEpochDay) + 1464.0, (int32_t) 1461, unusedRemainder);
|
||||
eyear = (int32_t) ClockMath::floorDivide((4.0*julianEpochDay) + 1464.0, (int32_t) 1461, &unusedRemainder);
|
||||
|
||||
// Compute the Julian calendar day number for January 1, eyear
|
||||
int32_t january1 = 365*(eyear-1) + ClockMath::floorDivide(eyear-1, (int32_t)4);
|
||||
|
@ -537,7 +537,7 @@ int32_t GregorianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month,
|
|||
// If the month is out of range, adjust it into range, and
|
||||
// modify the extended year value accordingly.
|
||||
if (month < 0 || month > 11) {
|
||||
eyear += ClockMath::floorDivide(month, 12, month);
|
||||
eyear += ClockMath::floorDivide(month, 12, &month);
|
||||
}
|
||||
|
||||
UBool isLeap = eyear%4 == 0;
|
||||
|
@ -580,7 +580,7 @@ int32_t GregorianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t mo
|
|||
// If the month is out of range, adjust it into range, and
|
||||
// modify the extended year value accordingly.
|
||||
if (month < 0 || month > 11) {
|
||||
extendedYear += ClockMath::floorDivide(month, 12, month);
|
||||
extendedYear += ClockMath::floorDivide(month, 12, &month);
|
||||
}
|
||||
|
||||
return isLeapYear(extendedYear) ? kLeapMonthLength[month] : kMonthLength[month];
|
||||
|
|
|
@ -33,28 +33,33 @@ int64_t ClockMath::floorDivide(int64_t numerator, int64_t denominator) {
|
|||
}
|
||||
|
||||
int32_t ClockMath::floorDivide(double numerator, int32_t denominator,
|
||||
int32_t& remainder) {
|
||||
double quotient;
|
||||
quotient = uprv_floor(numerator / denominator);
|
||||
remainder = (int32_t) (numerator - (quotient * denominator));
|
||||
int32_t* remainder) {
|
||||
// For an integer n and representable ⌊x/n⌋, ⌊RN(x/n)⌋=⌊x/n⌋, where RN is
|
||||
// rounding to nearest.
|
||||
double quotient = uprv_floor(numerator / denominator);
|
||||
// For doubles x and n, where n is an integer and ⌊x+n⌋ < 2³¹, the
|
||||
// expression `(int32_t) (x + n)` evaluated with rounding to nearest
|
||||
// differs from ⌊x+n⌋ if 0 < ⌈x⌉−x ≪ x+n, as `x + n` is rounded up to
|
||||
// n+⌈x⌉ = ⌊x+n⌋ + 1. Rewriting it as ⌊x⌋+n makes the addition exact.
|
||||
*remainder = (int32_t) (uprv_floor(numerator) - (quotient * denominator));
|
||||
return (int32_t) quotient;
|
||||
}
|
||||
|
||||
double ClockMath::floorDivide(double dividend, double divisor,
|
||||
double& remainder) {
|
||||
double* remainder) {
|
||||
// Only designed to work for positive divisors
|
||||
U_ASSERT(divisor > 0);
|
||||
double quotient = floorDivide(dividend, divisor);
|
||||
remainder = dividend - (quotient * divisor);
|
||||
*remainder = dividend - (quotient * divisor);
|
||||
// N.B. For certain large dividends, on certain platforms, there
|
||||
// is a bug such that the quotient is off by one. If you doubt
|
||||
// this to be true, set a breakpoint below and run cintltst.
|
||||
if (remainder < 0 || remainder >= divisor) {
|
||||
if (*remainder < 0 || *remainder >= divisor) {
|
||||
// E.g. 6.7317038241449352e+022 / 86400000.0 is wrong on my
|
||||
// machine (too high by one). 4.1792057231752762e+024 /
|
||||
// 86400000.0 is wrong the other way (too low).
|
||||
double q = quotient;
|
||||
quotient += (remainder < 0) ? -1 : +1;
|
||||
quotient += (*remainder < 0) ? -1 : +1;
|
||||
if (q == quotient) {
|
||||
// For quotients > ~2^53, we won't be able to add or
|
||||
// subtract one, since the LSB of the mantissa will be >
|
||||
|
@ -65,12 +70,12 @@ double ClockMath::floorDivide(double dividend, double divisor,
|
|||
// values give back an approximate answer rather than
|
||||
// crashing. For example, UDate values above a ~10^25
|
||||
// might all have a time of midnight.
|
||||
remainder = 0;
|
||||
*remainder = 0;
|
||||
} else {
|
||||
remainder = dividend - (quotient * divisor);
|
||||
*remainder = dividend - (quotient * divisor);
|
||||
}
|
||||
}
|
||||
U_ASSERT(0 <= remainder && remainder < divisor);
|
||||
U_ASSERT(0 <= *remainder && *remainder < divisor);
|
||||
return quotient;
|
||||
}
|
||||
|
||||
|
@ -106,10 +111,10 @@ void Grego::dayToFields(double day, int32_t& year, int32_t& month,
|
|||
// representation. We use 400-year, 100-year, and 4-year cycles.
|
||||
// For example, the 4-year cycle has 4 years + 1 leap day; giving
|
||||
// 1461 == 365*4 + 1 days.
|
||||
int32_t n400 = ClockMath::floorDivide(day, 146097, doy); // 400-year cycle length
|
||||
int32_t n100 = ClockMath::floorDivide(doy, 36524, doy); // 100-year cycle length
|
||||
int32_t n4 = ClockMath::floorDivide(doy, 1461, doy); // 4-year cycle length
|
||||
int32_t n1 = ClockMath::floorDivide(doy, 365, doy);
|
||||
int32_t n400 = ClockMath::floorDivide(day, 146097, &doy); // 400-year cycle length
|
||||
int32_t n100 = ClockMath::floorDivide(doy, 36524, &doy); // 100-year cycle length
|
||||
int32_t n4 = ClockMath::floorDivide(doy, 1461, &doy); // 4-year cycle length
|
||||
int32_t n1 = ClockMath::floorDivide(doy, 365, &doy);
|
||||
year = 400*n400 + 100*n100 + 4*n4 + n1;
|
||||
if (n100 == 4 || n1 == 4) {
|
||||
doy = 365; // Dec 31 at end of 4- or 400-year cycle
|
||||
|
@ -137,14 +142,14 @@ void Grego::dayToFields(double day, int32_t& year, int32_t& month,
|
|||
void Grego::timeToFields(UDate time, int32_t& year, int32_t& month,
|
||||
int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid) {
|
||||
double millisInDay;
|
||||
double day = ClockMath::floorDivide((double)time, (double)U_MILLIS_PER_DAY, millisInDay);
|
||||
double day = ClockMath::floorDivide((double)time, (double)U_MILLIS_PER_DAY, &millisInDay);
|
||||
mid = (int32_t)millisInDay;
|
||||
dayToFields(day, year, month, dom, dow, doy);
|
||||
}
|
||||
|
||||
int32_t Grego::dayOfWeek(double day) {
|
||||
int32_t dow;
|
||||
ClockMath::floorDivide(day + int{UCAL_THURSDAY}, 7, dow);
|
||||
ClockMath::floorDivide(day + int{UCAL_THURSDAY}, 7, &dow);
|
||||
return (dow == 0) ? UCAL_SATURDAY : dow;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ class ClockMath {
|
|||
* @return the floor of the quotient
|
||||
*/
|
||||
static int32_t floorDivide(double numerator, int32_t denominator,
|
||||
int32_t& remainder);
|
||||
int32_t* remainder);
|
||||
|
||||
/**
|
||||
* For a positive divisor, return the quotient and remainder
|
||||
|
@ -91,7 +91,7 @@ class ClockMath {
|
|||
* Calling with a divisor <= 0 is disallowed.
|
||||
*/
|
||||
static double floorDivide(double dividend, double divisor,
|
||||
double& remainder);
|
||||
double* remainder);
|
||||
};
|
||||
|
||||
// Useful millisecond constants
|
||||
|
|
|
@ -110,7 +110,7 @@ static UBool isGregorianLeap(int32_t year)
|
|||
*/
|
||||
int32_t IndianCalendar::handleGetMonthLength(int32_t eyear, int32_t month) const {
|
||||
if (month < 0 || month > 11) {
|
||||
eyear += ClockMath::floorDivide(month, 12, month);
|
||||
eyear += ClockMath::floorDivide(month, 12, &month);
|
||||
}
|
||||
|
||||
if (isGregorianLeap(eyear + INDIAN_ERA_START) && month == 0) {
|
||||
|
@ -210,7 +210,7 @@ int32_t IndianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UB
|
|||
|
||||
// If the month is out of range, adjust it into range, and adjust the extended year accordingly
|
||||
if (month < 0 || month > 11) {
|
||||
eyear += (int32_t)ClockMath::floorDivide(month, 12, month);
|
||||
eyear += (int32_t)ClockMath::floorDivide(month, 12, &month);
|
||||
}
|
||||
|
||||
if(month == 12){
|
||||
|
|
|
@ -110,7 +110,7 @@ int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType li
|
|||
UBool PersianCalendar::isLeapYear(int32_t year)
|
||||
{
|
||||
int32_t remainder;
|
||||
ClockMath::floorDivide(25 * year + 11, 33, remainder);
|
||||
ClockMath::floorDivide(25 * year + 11, 33, &remainder);
|
||||
return (remainder < 8);
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ int32_t PersianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t mont
|
|||
// If the month is out of range, adjust it into range, and
|
||||
// modify the extended year value accordingly.
|
||||
if (month < 0 || month > 11) {
|
||||
extendedYear += ClockMath::floorDivide(month, 12, month);
|
||||
extendedYear += ClockMath::floorDivide(month, 12, &month);
|
||||
}
|
||||
|
||||
return isLeapYear(extendedYear) ? kPersianLeapMonthLength[month] : kPersianMonthLength[month];
|
||||
|
@ -169,7 +169,7 @@ int32_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U
|
|||
// If the month is out of range, adjust it into range, and
|
||||
// modify the extended year value accordingly.
|
||||
if (month < 0 || month > 11) {
|
||||
eyear += ClockMath::floorDivide(month, 12, month);
|
||||
eyear += ClockMath::floorDivide(month, 12, &month);
|
||||
}
|
||||
|
||||
int32_t julianDay = PERSIAN_EPOCH - 1 + 365 * (eyear - 1) + ClockMath::floorDivide(8 * eyear + 21, 33);
|
||||
|
|
|
@ -518,9 +518,8 @@ SimpleTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingT
|
|||
}
|
||||
|
||||
rawOffsetGMT = getRawOffset();
|
||||
int32_t year, month, dom, dow;
|
||||
double day = uprv_floor(date / U_MILLIS_PER_DAY);
|
||||
int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
|
||||
int32_t year, month, dom, dow, millis;
|
||||
int32_t day = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis);
|
||||
|
||||
Grego::dayToFields(day, year, month, dom, dow);
|
||||
|
||||
|
@ -549,8 +548,7 @@ SimpleTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingT
|
|||
}
|
||||
}
|
||||
if (recalc) {
|
||||
day = uprv_floor(date / U_MILLIS_PER_DAY);
|
||||
millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
|
||||
day = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis);
|
||||
Grego::dayToFields(day, year, month, dom, dow);
|
||||
savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
|
||||
(uint8_t) dow, millis,
|
||||
|
|
|
@ -729,9 +729,8 @@ void TimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
|
|||
// (with 7 args) twice when local == true and DST is
|
||||
// detected in the initial call.
|
||||
for (int32_t pass=0; ; ++pass) {
|
||||
int32_t year, month, dom, dow;
|
||||
double day = uprv_floor(date / U_MILLIS_PER_DAY);
|
||||
int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
|
||||
int32_t year, month, dom, dow, millis;
|
||||
double day = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis);
|
||||
|
||||
Grego::dayToFields(day, year, month, dom, dow);
|
||||
|
||||
|
|
|
@ -97,6 +97,7 @@ CalendarRegressionTest::runIndexedTest( int32_t index, UBool exec, const char* &
|
|||
CASE(53,TestIslamicCalOverflow);
|
||||
CASE(54,TestWeekOfYear13548);
|
||||
CASE(55,Test13745);
|
||||
CASE(56,TestUTCWrongAMPM22023);
|
||||
default: name = ""; break;
|
||||
}
|
||||
}
|
||||
|
@ -3089,6 +3090,105 @@ void CalendarRegressionTest::TestIslamicCalOverflow(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void CalendarRegressionTest::VerifyGetStayInBound(double time) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LocalPointer<Calendar> utc(
|
||||
Calendar::createInstance(TimeZone::createTimeZone(u"UTC"), status));
|
||||
utc->setTime(time, status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln("UTC setTime(%e, status)", time);
|
||||
}
|
||||
|
||||
status = U_ZERO_ERROR;
|
||||
LocalPointer<Calendar> gmt(Calendar::createInstance(
|
||||
*TimeZone::getGMT(), status));
|
||||
gmt->setTime(time, status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln("UTC setTime(%e, status)", time);
|
||||
}
|
||||
|
||||
status = U_ZERO_ERROR;
|
||||
int32_t value = utc->get(UCAL_AM_PM, status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln("UTC %e get(UCAL_AM_PM, status)", time);
|
||||
}
|
||||
if (value != UCAL_AM && value != UCAL_PM) {
|
||||
errln("UTC %e UCAL_AM_PM should be either UCAL_AM | UCAL_PM but is %d",
|
||||
time, value);
|
||||
}
|
||||
|
||||
status = U_ZERO_ERROR;
|
||||
value = gmt->get(UCAL_AM_PM, status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln("GMT %e get(UCAL_AM_PM, status)", time);
|
||||
}
|
||||
if (value != UCAL_AM && value != UCAL_PM) {
|
||||
errln("GMT %e UCAL_AM_PM should be either UCAL_AM | UCAL_PM but is %d",
|
||||
time, value);
|
||||
}
|
||||
|
||||
int32_t fields[] = {
|
||||
UCAL_WEEK_OF_YEAR,
|
||||
UCAL_YEAR_WOY,
|
||||
UCAL_DAY_OF_MONTH,
|
||||
UCAL_WEEK_OF_MONTH,
|
||||
UCAL_DAY_OF_WEEK_IN_MONTH,
|
||||
UCAL_MILLISECONDS_IN_DAY,
|
||||
UCAL_MILLISECOND,
|
||||
UCAL_SECOND,
|
||||
UCAL_MINUTE,
|
||||
UCAL_HOUR_OF_DAY,
|
||||
UCAL_AM_PM,
|
||||
UCAL_HOUR,
|
||||
UCAL_ZONE_OFFSET,
|
||||
UCAL_DST_OFFSET
|
||||
};
|
||||
for (auto& f : fields) {
|
||||
UnicodeString info("Fields = ");
|
||||
info += f;
|
||||
status = U_ZERO_ERROR;
|
||||
UCalendarDateFields field = static_cast<UCalendarDateFields>(f);
|
||||
value = utc->get(field, status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln("UTC %e get(%d)", time, field);
|
||||
}
|
||||
int32_t min = utc->getMinimum(field);
|
||||
int32_t max = utc->getMaximum(field);
|
||||
if (value < min) {
|
||||
errln("UTC %e get(%d) < getMinimum(%d) : %d < %d", time, field,
|
||||
field, value, min);
|
||||
}
|
||||
if (max < value) {
|
||||
errln("UTC %e getMaximum(%d) < get(%d) : %d < %d", time, field,
|
||||
field, max, value);
|
||||
}
|
||||
|
||||
status = U_ZERO_ERROR;
|
||||
value = gmt->get(field, status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln("GMT %e get(%d)", time, field);
|
||||
}
|
||||
min = gmt->getMinimum(field);
|
||||
max = gmt->getMaximum(field);
|
||||
if (value < min) {
|
||||
errln("GMT %e get(%d) < getMinimum(%d) : %d < %d", time, field,
|
||||
field, value, min);
|
||||
}
|
||||
if (max < value) {
|
||||
errln("GMT %e getMaximum(%d) < get(%d) : %d < %d", time, field,
|
||||
field, max, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalendarRegressionTest::TestUTCWrongAMPM22023(void) {
|
||||
VerifyGetStayInBound(-1);
|
||||
VerifyGetStayInBound(0);
|
||||
VerifyGetStayInBound(-1e-8);
|
||||
VerifyGetStayInBound(-1e-9);
|
||||
VerifyGetStayInBound(-1e-15);
|
||||
}
|
||||
|
||||
void CalendarRegressionTest::TestWeekOfYear13548(void) {
|
||||
int32_t year = 2000;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
|
|
@ -81,12 +81,14 @@ public:
|
|||
void TestPersianCalOverflow(void);
|
||||
void TestIslamicCalOverflow(void);
|
||||
void TestWeekOfYear13548(void);
|
||||
void TestUTCWrongAMPM22023(void);
|
||||
|
||||
void Test13745(void);
|
||||
|
||||
void printdate(GregorianCalendar *cal, const char *string);
|
||||
void dowTest(UBool lenient) ;
|
||||
|
||||
void VerifyGetStayInBound(double test_value);
|
||||
|
||||
static UDate getAssociatedDate(UDate d, UErrorCode& status);
|
||||
static UDate makeDate(int32_t y, int32_t m = 0, int32_t d = 0, int32_t hr = 0, int32_t min = 0, int32_t sec = 0);
|
||||
|
|
|
@ -130,6 +130,7 @@ void DateFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &nam
|
|||
TESTCASE_AUTO(TestParseRegression13744);
|
||||
TESTCASE_AUTO(TestAdoptCalendarLeak);
|
||||
TESTCASE_AUTO(Test20741_ABFields);
|
||||
TESTCASE_AUTO(Test22023_UTCWithMinusZero);
|
||||
|
||||
TESTCASE_AUTO_END;
|
||||
}
|
||||
|
@ -5726,6 +5727,22 @@ void DateFormatTest::Test20741_ABFields() {
|
|||
}
|
||||
}
|
||||
|
||||
void DateFormatTest::Test22023_UTCWithMinusZero() {
|
||||
IcuTestErrorCode status(*this, "Test22023_UTCWithMinusZero");
|
||||
Locale locale("en");
|
||||
SimpleDateFormat fmt("h a", locale, status);
|
||||
ASSERT_OK(status);
|
||||
fmt.adoptCalendar(Calendar::createInstance(
|
||||
TimeZone::createTimeZone("UTC"), locale, status));
|
||||
ASSERT_OK(status);
|
||||
FieldPositionIterator fp_iter;
|
||||
icu::UnicodeString formatted;
|
||||
// very small negative value in double cause it to be -0
|
||||
// internally and trigger the assertion and bug.
|
||||
fmt.format(-1e-9, formatted, &fp_iter, status);
|
||||
ASSERT_OK(status);
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
//eof
|
||||
|
|
|
@ -266,6 +266,7 @@ public:
|
|||
void TestParseRegression13744();
|
||||
void TestAdoptCalendarLeak();
|
||||
void Test20741_ABFields();
|
||||
void Test22023_UTCWithMinusZero();
|
||||
|
||||
private:
|
||||
UBool showParse(DateFormat &format, const UnicodeString &formattedString);
|
||||
|
|
Loading…
Add table
Reference in a new issue