From 2a5df236f5f6fbe64363fd73e458cd593c20521f Mon Sep 17 00:00:00 2001 From: Jugu Dannie Sundar Date: Tue, 5 Sep 2017 22:22:46 +0000 Subject: [PATCH] ICU-11632 icu4j changes for integer overflow in calendar support X-SVN-Rev: 40370 --- .../core/src/com/ibm/icu/util/Calendar.java | 126 +++++++++++++++++- .../test/calendar/CalendarRegressionTest.java | 21 +++ 2 files changed, 143 insertions(+), 4 deletions(-) diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/Calendar.java b/icu4j/main/classes/core/src/com/ibm/icu/util/Calendar.java index ea0aee26804..6529867e708 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/Calendar.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/Calendar.java @@ -1301,6 +1301,11 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable MAX_HOURS) { + millisInDay = computeMillisInDayLong(); + } else { + millisInDay = computeMillisInDay(); + } } if (stamp[ZONE_OFFSET] >= MINIMUM_USER_STAMP || @@ -5597,8 +5613,9 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable hourOfDayStamp) ? hourStamp : hourOfDayStamp; + + // Hours + if (bestStamp != UNSET) { + if (bestStamp == hourOfDayStamp) { + // Don't normalize here; let overflow bump into the next period. + // This is consistent with how we handle other fields. + millisInDay += internalGet(HOUR_OF_DAY); + } else { + // Don't normalize here; let overflow bump into the next period. + // This is consistent with how we handle other fields. + millisInDay += internalGet(HOUR); + millisInDay += 12 * internalGet(AM_PM); // Default works for unset AM_PM + } + } + + // We use the fact that unset == 0; we start with millisInDay + // == HOUR_OF_DAY. + millisInDay *= 60; + millisInDay += internalGet(MINUTE); // now have minutes + millisInDay *= 60; + millisInDay += internalGet(SECOND); // now have seconds + millisInDay *= 1000; + millisInDay += internalGet(MILLISECOND); // now have millis + + return millisInDay; + } + + /** * This method can assume EXTENDED_YEAR has been set. * @param millis milliseconds of the date fields (local midnight millis) * @param millisInDay milliseconds of the time fields; may be out * or range. * @return total zone offset (raw + DST) for the given moment - * @stable ICU 2.0 + * @deprecated ICU 60 */ + @Deprecated protected int computeZoneOffset(long millis, int millisInDay) { int[] offsets = new int[2]; long wall = millis + millisInDay; @@ -5689,6 +5754,59 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable -6*60*60*1000 : offsetDelta; + if (offsetDelta < 0) { + sawRecentNegativeShift = true; + // Negative shift within last 6 hours. When WALLTIME_FIRST is used and the given wall time falls + // into the repeated time range, use offsets before the transition. + // Note: If it does not fall into the repeated time range, offsets remain unchanged below. + zone.getOffset(wall + offsetDelta, true, offsets); + } + } + if (!sawRecentNegativeShift && skippedWallTime == WALLTIME_FIRST) { + // When skipped wall time option is WALLTIME_FIRST, + // recalculate offsets from the resolved time (non-wall). + // When the given wall time falls into skipped wall time, + // the offsets will be based on the zone offsets AFTER + // the transition (which means, earliest possibe interpretation). + long tgmt = wall - (offsets[0] + offsets[1]); + zone.getOffset(tgmt, false, offsets); + } + } + return offsets[0] + offsets[1]; + } + /** * Compute the Julian day number as specified by this calendar's fields. * @stable ICU 2.0 diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java index 379d9137014..71ed611ef11 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/calendar/CalendarRegressionTest.java @@ -2479,5 +2479,26 @@ public class CalendarRegressionTest extends com.ibm.icu.dev.test.TestFmwk { assertEquals("Fail: dateformat doesn't interpret calendar correctly", expectedFormat, actualFormat); } + @Test + public void TestTicket11632() { + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(Calendar.HOUR, 596); + // hour value set upto 596 lies within the integer range for millisecond calculations + assertEquals("Incorrect time for integer range milliseconds","Sun Jan 25 20:00:00 PST 1970", cal.getTime().toString()); + cal.clear(); + // hour value set above 596 lies outside the integer range for millisecond calculations. This will invoke + // the long version of the compute millis in day method in the ICU internal API + cal.set(Calendar.HOUR, 597); + assertEquals("Incorrect time for long range milliseconds","Sun Jan 25 21:00:00 PST 1970", cal.getTime().toString()); + cal.clear(); + cal.set(Calendar.HOUR, 597); + cal.set(Calendar.MINUTE, 60*24); + assertEquals("Incorrect time for long range milliseconds","Mon Jan 26 21:00:00 PST 1970", cal.getTime().toString()); + cal.clear(); + cal.set(Calendar.HOUR_OF_DAY, 597); + assertEquals("Incorrect time for long range milliseconds","Sun Jan 25 21:00:00 PST 1970", cal.getTime().toString()); + } + } //eof