Skip to content
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

Merged
merged 2 commits into from Dec 8, 2020

Conversation

findepi
Copy link
Member

@findepi findepi commented Feb 25, 2019

Rel #6209

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);
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

@electrum electrum Feb 26, 2019

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);

Copy link
Member

@electrum electrum Feb 26, 2019

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).

Copy link
Member Author

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.

Copy link
Member

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());
Copy link
Member

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());
Copy link
Member

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))
Copy link
Member

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.

Copy link
Member Author

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)?

Copy link
Member

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.

@dain dain changed the title Represent TIMESTAMP W/TZ as ZonedDateTime in JDBC [WIP] Represent TIMESTAMP W/TZ as ZonedDateTime in JDBC Apr 22, 2019
@kokosing kokosing added WIP and removed WIP labels May 8, 2019
@guyco33
Copy link
Member

guyco33 commented May 27, 2019

I found this PR valuable.
Although the implementation of getObject is not backward compatible and it's not 100% consistent with respect to offset-literal-timestamps with JVM timezone, I found it much valuables due to the ability to perform timezone conversions with AT TIME ZONE.

Here is an example to illustrate this:
JVM timezone is set to "America/Bahia_Banderas" (UTC-5)

SQL text Before After
SELECT current_timezone() America/Bahia_Banderas America/Bahia_Banderas
SELECT timestamp '2019-05-27 10:00:00' 2019-05-27 10:00:00 2019-05-27 10:00:00
SELECT timestamp '2019-05-27 10:00:00 +05:17' 2019-05-26 23:43:00 2019-05-27 10:00:00
SELECT timestamp '2019-05-27 10:00:00 UTC' 2019-05-27 05:00:00 2019-05-27 10:00:00
SELECT timestamp '2019-05-27 10:00:00' AT TIME ZONE 'UTC' 2019-05-27 10:00:00 2019-05-27 15:00:00
SELECT timestamp '2019-05-27 10:00:00 UTC' AT TIME ZONE 'UTC' 2019-05-27 05:00:00 2019-05-27 10:00:00
SELECT ts_col FROM tbl (tbl.ts_col is a timestamp column) 2019-05-27 10:00:00 2019-05-27 10:00:00
SELECT ts_col AT TIME ZONE 'UTC' FROM tbl 2019-05-27 10:00:00 2019-05-27 15:00:00

@findepi @electrum @dain

@findepi findepi changed the title [WIP] Represent TIMESTAMP W/TZ as ZonedDateTime in JDBC [WIP] Represent TIMESTAMP WITH TIME ZONE as ZonedDateTime in JDBC Feb 14, 2020
@findepi findepi changed the title [WIP] Represent TIMESTAMP WITH TIME ZONE as ZonedDateTime in JDBC Represent TIMESTAMP WITH TIME ZONE as ZonedDateTime in JDBC Feb 14, 2020
@findepi findepi marked this pull request as ready for review February 14, 2020 17:53
@findepi findepi removed the WIP label Feb 14, 2020
@findepi findepi force-pushed the jdbc-with-time-zone branch 2 times, most recently from 5aefe41 to d5e43f9 Compare February 17, 2020 07:59
ZonedDateTime zonedDateTime = getTimestampWithTimeZone(columnIndex);
verify(zonedDateTime != null);
// point in time
Timestamp timestamp = new Timestamp(zonedDateTime.toEpochSecond() * 1000);
Copy link
Member

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())

Copy link
Member Author

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

@findepi findepi force-pushed the jdbc-with-time-zone branch 2 times, most recently from e4fe71d to 406270f Compare November 30, 2020 13:29
@findepi
Copy link
Member Author

findepi commented Nov 30, 2020

Based on #6152, other than that hopefully ready for review.

@findepi findepi changed the title Represent TIMESTAMP WITH TIME ZONE as ZonedDateTime in JDBC Support TIMESTAMP WITH TIME ZONE as ZonedDateTime in JDBC Dec 4, 2020
@findepi findepi force-pushed the jdbc-with-time-zone branch 2 times, most recently from e5b86e8 to 469690d Compare December 4, 2020 16:06
@findepi findepi added the jdbc Relates to Trino JDBC driver label Dec 4, 2020
@findepi findepi added the enhancement New feature or request label Dec 4, 2020
@findepi findepi force-pushed the jdbc-with-time-zone branch 2 times, most recently from 3b2568f to de66512 Compare December 5, 2020 07:52
@findepi
Copy link
Member Author

findepi commented Dec 5, 2020

Depends on #6208, but otherwise ready for review.

@findepi findepi merged commit 2d92d05 into trinodb:master Dec 8, 2020
@findepi findepi deleted the jdbc-with-time-zone branch December 8, 2020 12:48
@findepi findepi mentioned this pull request Dec 8, 2020
9 tasks
@findepi findepi added this to the 348 milestone Dec 8, 2020
rice668 pushed a commit to rice668/trino that referenced this pull request Nov 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla-signed enhancement New feature or request jdbc Relates to Trino JDBC driver
Development

Successfully merging this pull request may close these issues.

None yet

6 participants