Permalink
Browse files

fix: improve handling of DATE columns around DST dates in binary tran…

…sfer

millis-tz.getOffest(millis) does not work for DST-enabled time zones, thus TimestampUtils.guessTimestamp should be used to avoid cases when -tz.getOffset() would skip past DST change
  • Loading branch information...
vlsi committed Jan 11, 2016
1 parent d622a9f commit 642b48a787098a6c5a068710bdbbf9f1b11f3aac
@@ -680,15 +680,17 @@ public Date toDateBin(TimeZone tz, byte[] bytes) throws PSQLException {
}
long secs = toJavaSecs(days * 86400L);
long millis = secs * 1000L;
int offset = tz.getOffset(millis);
// Here be dragons: backend did not provide us the timezone, so we guess the actual point in
// time
millis = guessTimestamp(millis, tz);
if (millis <= PGStatement.DATE_NEGATIVE_SMALLER_INFINITY) {
millis = PGStatement.DATE_NEGATIVE_INFINITY;
offset = 0;
} else if (millis >= PGStatement.DATE_POSITIVE_SMALLER_INFINITY) {
millis = PGStatement.DATE_POSITIVE_INFINITY;
offset = 0;
}
return new Date(millis - offset);
return new Date(millis);
}
private static TimeZone getDefaultTz() {
@@ -727,15 +729,17 @@ public Time toTimeBin(TimeZone tz, byte[] bytes) throws PSQLException {
if (bytes.length == 12) {
timeOffset = ByteConverter.int4(bytes, 8);
timeOffset *= -1000;
millis -= timeOffset;
} else {
if (tz == null) {
tz = getDefaultTz();
}
timeOffset = tz.getOffset(millis);
// Here be dragons: backend did not provide us the timezone, so we guess the actual point in
// time
millis = guessTimestamp(millis, tz);
}
millis -= timeOffset;
return convertToTime(millis, tz); // Ensure date part is 1970-01-01
}
@@ -818,7 +818,13 @@ public void testLocalTimestampsInAmericaAdak() throws Exception {
localTimestamps("America/Adak"); // It is something like GMT-10..GMT-9
}
private String setTimeTo00_00_00(String timestamp) {
return timestamp.substring(0, 11) + "00:00:00";
}
public void localTimestamps(String timeZone) throws Exception {
TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
final String testDateFormat = "yyyy-MM-dd HH:mm:ss";
final List<String> datesToTest = Arrays.asList("2015-09-03 12:00:00", "2015-06-30 23:59:58",
"1997-06-30 23:59:59", "1997-07-01 00:00:00", "2012-06-30 23:59:59", "2012-07-01 00:00:00",
@@ -841,12 +847,15 @@ public void localTimestamps(String timeZone) throws Exception {
for (int i = 0; i < datesToTest.size(); i++) {
stmt.execute(
"insert into testtimezone (ts, seq) values ('" + datesToTest.get(i) + "', " + i + ")");
"insert into testtimezone (ts, d, seq) values ("
+ "'" + datesToTest.get(i) + "'"
+ ", '" + setTimeTo00_00_00(datesToTest.get(i)) + "'"
+ ", " + i + ")");
}
// Different timezone test should have different sql text, so we test both text and binary modes
PreparedStatement pstmt =
con.prepareStatement("SELECT ts FROM testtimezone order by seq /*" + timeZone + "*/");
con.prepareStatement("SELECT ts, d FROM testtimezone order by seq /*" + timeZone + "*/");
Calendar expectedTimestamp = Calendar.getInstance();
@@ -857,6 +866,7 @@ public void localTimestamps(String timeZone) throws Exception {
for (int j = 0; rs.next(); j++) {
String testDate = datesToTest.get(j);
Date getDate = rs.getDate(1);
Date getDateFromDateColumn = rs.getDate(2);
Timestamp getTimestamp = rs.getTimestamp(1);
String getString = rs.getString(1);
Time getTime = rs.getTime(1);
@@ -877,10 +887,24 @@ public void localTimestamps(String timeZone) throws Exception {
expectedTimestamp.set(Calendar.SECOND, 0);
assertEquals(
"getDate: " + testDate + ", transfer format: " + (i == 0 ? "text" : "binary")
"TIMESTAMP -> getDate: " + testDate + ", transfer format: " + (i == 0 ? "text" : "binary")
+ ", timeZone: " + timeZone,
sdf.format(expectedTimestamp.getTimeInMillis()), sdf.format(getDate));
String expectedDateFromDateColumn = setTimeTo00_00_00(testDate);
if ("Atlantic/Azores".equals(timeZone) && testDate.startsWith("2000-03-26")) {
// Atlantic/Azores does not have 2000-03-26 00:00:00
// They go right to 2000-03-26 01:00:00 due to DST.
// Vladimir Sitnikov: I have no idea how do they represent 2000-03-26 00:00:00 :(
// So the assumption is 2000-03-26 01:00:00 is the expected for that time zone
expectedDateFromDateColumn = "2000-03-26 01:00:00";
}
assertEquals(
"DATE -> getDate: " + expectedDateFromDateColumn + ", transfer format: " + (i == 0 ? "text" : "binary")
+ ", timeZone: " + timeZone,
expectedDateFromDateColumn, sdf.format(getDateFromDateColumn));
expectedTimestamp.setTime(sdf.parse(testDate));
expectedTimestamp.set(Calendar.YEAR, 1970);
expectedTimestamp.set(Calendar.MONTH, 0);

0 comments on commit 642b48a

Please sign in to comment.