Skip to content

Commit

Permalink
fix: date rounding errors for dates before 1970
Browse files Browse the repository at this point in the history
millis % ONEDAY should be reworked to floorMod(millis, ONEDAY)
Regular division "rounds" towards zero, however that is not what we want for dates
Division "towards zero" results to adding a day for dates with "negative" long values.

So we should use floorDiv/floorMod to get date/time components

see #1499
  • Loading branch information
vlsi committed Jun 18, 2019
1 parent 45ce14f commit b565389
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 339 deletions.
18 changes: 7 additions & 11 deletions pgjdbc/src/main/java/org/postgresql/jdbc/TimestampUtils.java
Expand Up @@ -1232,12 +1232,7 @@ public Date convertToDate(long millis, TimeZone tz) {
if (tz == null) {
tz = getDefaultTz();
}
/*
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())) {
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
Expand Down Expand Up @@ -1285,7 +1280,7 @@ public Time convertToTime(long millis, TimeZone tz) {
int offset = tz.getRawOffset();
millis += offset;
// 2) Truncate year, month, day. Day is always 86400 seconds, no matter what leap seconds are
millis = millis % ONEDAY;
millis = floorMod(millis, ONEDAY);
// 2) Now millis is 1970 1 Jan 15:40 UTC, however we need that in GMT+02:00, so subtract some
// offset
millis -= offset;
Expand Down Expand Up @@ -1419,15 +1414,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) {
private 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;
}

private static long floorMod(long x, long y) {
return x - floorDiv(x, y) * y;
}
}

0 comments on commit b565389

Please sign in to comment.