-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support TIMESTAMP WITH TIME ZONE as ZonedDateTime in JDBC #307
Conversation
if (columnInfo.getColumnTypeName().equalsIgnoreCase("timestamp with time zone")) { | ||
try { | ||
return new Timestamp(TIMESTAMP_WITH_TIME_ZONE_FORMATTER.parseMillis(String.valueOf(value))); | ||
return TIMESTAMP_WITH_TIME_ZONE_FORMATTER.parse(String.valueOf(value), ZonedDateTime::from); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The JDBC 4.3 spec doesn't seem to define the correct behavior for timestamp with time zone like it does with timestamp (required to return java.sql.Timestamp). Do you know what other drivers do?
One interesting thing is the spec only ever talks about OffsetDateTime
, likely because the SQL spec does not cover political time zones.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dain good question. As discussed offline, this pertains to ResultSet#getObject(col)
(i.e. without Class
specifier).
From the database I have some working knowledge of, Oracle implements TIMESTAMP WITH TIME ZONE correctly, and probably Teradata as well.
I don't know yet what Oracle driver returns on getObject(col)
(i would assume something compatible with what they picked when implementing JDBC 3...)
I think in Presto, getObject(col)
should not return OffsetDateTime
. This would be misleading, as the underlying value is not representable as OffsetDateTime
without information loss.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PostgreSQL JDBC 42.2.5
returns java.sql.Timestamp
by default and supports OffsetDateTime
but not ZonedDateTime
. The implementation is ... interesting:
Timestamp timestampValue = getTimestamp(columnIndex);
...
// Postgres stores everything in UTC and does not keep original time zone
OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(timestampValue.toInstant(), ZoneOffset.UTC);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PostgreSQL docs:
For timestamp with time zone, the internally stored value is always in UTC
When a timestamp with time zone value is output, it is always converted from UTC to the current timezone zone, and displayed as local time in that zone
This seems to violate the SQL standard (and is different than Oracle), and the documentation doesn't mention it (and it mentions the standard in several other places).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, PostgreSQL'sTIMESTAMP WITH TIME ZONE
is not standard-compliant. Their type represents a point in time (Instant in Java) instead of (point in time, zone) pair (ZonedDateTime in Java).
Thus, i don't see need to align with PostgreSQL driver behavior for this feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, returning ZonedDateTime
seems correct for Presto.
} | ||
} | ||
|
||
throw new IllegalArgumentException("Expected column to be a timestamp type but is " + columnInfo.getColumnTypeName()); | ||
throw new IllegalArgumentException("Expected column to be a timestamp with time zone type but is " + columnInfo.getColumnTypeName()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's put quotes or brackets around the timestamp with the zone
type name
@@ -331,16 +326,28 @@ private Timestamp getTimestamp(int columnIndex, DateTimeZone localTimeZone) | |||
} | |||
} | |||
|
|||
throw new IllegalArgumentException("Expected column to be a timestamp type but is " + columnInfo.getColumnTypeName()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be SQLException
? I see we use IAE
already, but I think that's a bug
assertThrows(() -> rs.getDate(column)); | ||
assertThrows(() -> rs.getTime(column)); | ||
assertEquals(rs.getTimestamp(column), Timestamp.valueOf(LocalDateTime.of(2018, 2, 13, 6, 14, 15, 227_000_000))); // TODO this should fail, or represent TIMESTAMP '2018-02-13 13:14:15.227' | ||
assertThrows(() -> rs.getTimestamp(column)); // this could also return java.sql.Timestamp.valueOf(LocalDateTime.of(2018, 2, 13, 13, 14, 15, 227_000_000)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should continue to allow calling getTimestamp()
to not break applications.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, and how do we deal with timestamps that are nor representable as java.sql.Timestamp
(ie values when JVM zone had forward offset shift)?
just return some near value (as java.sql.Timestamp.valueOf(LocalDateTime)
probably does)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think using Timestamp.valueOf(LocalDateTime)
is reasonable here.
That code path goes through the legacy Timestamp
constructor which takes fields and calls into some sun.util.calendar
code, so it's not clear what it does, but it seems fine to use the method since it exists in the JDK.
presto-jdbc/src/main/java/io/prestosql/jdbc/PrestoResultSet.java
Outdated
Show resolved
Hide resolved
presto-jdbc/src/main/java/io/prestosql/jdbc/PrestoResultSet.java
Outdated
Show resolved
Hide resolved
presto-jdbc/src/main/java/io/prestosql/jdbc/PrestoResultSet.java
Outdated
Show resolved
Hide resolved
I found this PR valuable. Here is an example to illustrate this:
|
e42a351
to
ca9bdb2
Compare
ca9bdb2
to
8e0aa15
Compare
5aefe41
to
d5e43f9
Compare
d5e43f9
to
65c2be3
Compare
presto-jdbc/src/main/java/io/prestosql/jdbc/PrestoResultSet.java
Outdated
Show resolved
Hide resolved
ZonedDateTime zonedDateTime = getTimestampWithTimeZone(columnIndex); | ||
verify(zonedDateTime != null); | ||
// point in time | ||
Timestamp timestamp = new Timestamp(zonedDateTime.toEpochSecond() * 1000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SECONDS.toMillis(zonedDateTime.toEpochSecond())
But how is this different than
Timestamp.valueOf(zonedDateTime.toLocalDateTime())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is different semantics.
Timestamp.valueOf(zonedDateTime.toLocalDateTime())
is like java.time.ZonedDateTime#withZoneSameLocal
current code is like java.time.ZonedDateTime#withZoneSameInstant
e4fe71d
to
406270f
Compare
Based on #6152, other than that hopefully ready for review. |
e5b86e8
to
469690d
Compare
469690d
to
5545243
Compare
3b2568f
to
de66512
Compare
Depends on #6208, but otherwise ready for review. |
de66512
to
e558de2
Compare
Rel #6209