Skip to content
Permalink
Browse files

fix #1499 rounding for timestamps truncated to dates before 1970 (#1502)

* fix #1499 rounding for timestamps truncated to dates before 1970

* Implement floorDiv until we drop support for Java 1.7
  • Loading branch information...
davecramer committed Jun 17, 2019
1 parent 1970c4a commit c9a70782b3818609ca29e19d1a4aa0af89d96382
@@ -1232,7 +1232,12 @@ public Date convertToDate(long millis, TimeZone tz) {
if (tz == null) {
tz = getDefaultTz();
}
if (isSimpleTimeZone(tz.getID())) {
/*
the following math does not work for negative times
Punt and use Calendar for negative times.
This probably introduces a performance regression for times before 1970
*/
if ( isSimpleTimeZone(tz.getID())) {
// Truncate to 00:00 of the day.
// Suppose the input date is 7 Jan 15:40 GMT+02:00 (that is 13:40 UTC)
// We want it to become 7 Jan 00:00 GMT+02:00
@@ -1241,20 +1246,22 @@ public Date convertToDate(long millis, TimeZone tz) {
millis += offset;
// 2) Truncate hours, minutes, etc. Day is always 86400 seconds, no matter what leap seconds
// are
millis = millis / ONEDAY * ONEDAY;
millis = floorDiv(millis, ONEDAY) * ONEDAY;
// 2) Now millis is 7 Jan 00:00 UTC, however we need that in GMT+02:00, so subtract some
// offset
millis -= offset;
// Now we have brand-new 7 Jan 00:00 GMT+02:00
return new Date(millis);
}

Calendar cal = calendarWithUserTz;
cal.setTimeZone(tz);
cal.setTimeInMillis(millis);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);

return new Date(cal.getTimeInMillis());
}

@@ -1411,4 +1418,16 @@ public static TimeZone parseBackendTimeZone(String timeZone) {
}
return TimeZone.getTimeZone(timeZone);
}

/*
provide our own as this is not available until 1.8
*/
public static long floorDiv(long x, long y) {
long r = x / y;
// if the signs are different and modulo not zero, round down
if ((x ^ y) < 0 && (r * y != x)) {
r--;
}
return r;
}
}
@@ -47,6 +47,8 @@ public TimestampTest(BinaryMode binaryMode) {
setBinaryMode(binaryMode);
}

private TimeZone currentTZ;

@Parameterized.Parameters(name = "binary = {0}")
public static Iterable<Object[]> data() {
Collection<Object[]> ids = new ArrayList<Object[]>();
@@ -62,13 +64,15 @@ public void setUp() throws Exception {
TestUtil.createTable(con, TSWTZ_TABLE, "ts timestamp with time zone");
TestUtil.createTable(con, TSWOTZ_TABLE, "ts timestamp without time zone");
TestUtil.createTable(con, DATE_TABLE, "ts date");
currentTZ = TimeZone.getDefault();
}

@Override
public void tearDown() throws SQLException {
TestUtil.dropTable(con, TSWTZ_TABLE);
TestUtil.dropTable(con, TSWOTZ_TABLE);
TestUtil.dropTable(con, DATE_TABLE);
TimeZone.setDefault(currentTZ);
super.tearDown();
}

@@ -555,6 +559,22 @@ public void testSetTimestampWOTZ() throws SQLException {
stmt.close();
}

/*
Times before 1970 are negative in Java.
the convert to date does some math that assumes times are positive.
This test assures that the convertToDate still works properly with negative times
*/
@Test
public void TestTimeStampBefore1970() {

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
TimestampUtils tsu = ((BaseConnection) con).getTimestampUtils();

Date date = tsu.convertToDate(Timestamp.valueOf("1969-01-10 12:00:00").getTime(), TimeZone.getDefault());

assertEquals(new GregorianCalendar(1969, Calendar.JANUARY, 10).getTime().getTime(), date.getTime() );
}

/*
* Helper for the TimestampTests. It tests what should be in the db
*/

0 comments on commit c9a7078

Please sign in to comment.
You can’t perform that action at this time.