[ 
https://issues.apache.org/jira/browse/CAY-2701?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Andrus Adamchik closed CAY-2701.
--------------------------------
    Resolution: Fixed

> MySQL DST-related LocalDateTime issues
> --------------------------------------
>
>                 Key: CAY-2701
>                 URL: https://issues.apache.org/jira/browse/CAY-2701
>             Project: Cayenne
>          Issue Type: Bug
>    Affects Versions: 4.0.2, 4.1, 4.2.M2
>         Environment: MySQL 5.7.x, table column with type "datetime"
> JDBC Driver: mysql:mysql-connector-java:5.1.46
> JVM timezone: "America/New_York"
>            Reporter: Andrus Adamchik
>            Priority: Major
>             Fix For: 5.0-M2
>
>
> Just ran into a whole collection of annoying problems related to 
> daylight-savings time. As mentioned in the "Environment" section, the test 
> environment is MySQL 5.7.x, mysql:mysql-connector-java:5.1.46, 
> "America/New_York" timezone. Some of the issues described here are universal, 
> others are DB and driver and JVM TZ sensitive.
> h2. Problem 1: Lossy conversion from LocalDateTime to Timestamp
> Say a LocalDateTime value corresponds to a UTC timezone (so no DST there), 
> and want to save a value of "2021-03-14T02:00:00". The JVM is located in 
> "America/New_York" timezone, where this specific hour ("2021-03-14T02:XX:XX") 
> is skipped due to EST to EDT switchover. This combination prevents Cayenne 
> from saving such as local date correctly because LocalDateTime to Timestamp 
> conversion (used by Cayenne to bind datetime value to JDBC) would actually 
> use the JVM TZ (!!) and the hour will be increased by 1 (so 
> "2021-03-14T03:35:00" will be saved). Here is a JDBC-agnostic test to 
> demonstreate that:
> {noformat}
> @Test
> public void test() {
>     TimeZone defaultTz = TimeZone.getDefault();
>     TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
>     try {
>         LocalDateTime dt = LocalDateTime.parse("2021-03-14T02:35:00");
>         assertEquals(dt, Timestamp.valueOf(dt).toLocalDateTime());
>     } finally {
>         TimeZone.setDefault(defaultTz);
>     }
> }
> {noformat}
> There seems to be on way around it (this is an expected java.sql.Timestamp 
> behavior!!), except to replace LocalDateTime-to-Timestamp conversion with 
> LocalDateTime-to-String conversion. This causes some downstream driver 
> issues. MySQL 5.1.x driver throws on "PreparedStatement.setObject(i, string, 
> Types.TIMESTAMP)", and "setString(..)" should be called instead. Not sure 
> about other DBs and 8.x MySQL driver.
> With the above in mind, LocalDateTime ValueObjectType should be reimplemented 
> as an ExtendedType, and we need to test it across the DBs.
> h2. Problem 2: MySQL 5.1.x driver will add an hour on read
> The ExtendedType above allows to write LocalDateTime properly, DST or not. 
> But when reading it back, MySQL Driver interferes. When reading a column that 
> is a "datetime" as String, it first does a conversion to Timestamp, and then 
> converts it to String. So again - an hour is added unexpectedly.
> There's no Cayenne-side fix for that. But the DB connection string must 
> contain "noDatetimeStringSync=true". This seems to be fixed in the 8.x 
> driver, so this flag is no longer required there.
> _(Update for MySQL 8, driver version 9.1.0: This is still an issue, and an 
> hour is still added. But_ _"noDatetimeStringSync=true"_ _is no longer 
> required for the workaround described below - reading datetime as a String 
> before converting it to a LocalDate)_
> h2. Workarounds / Best Practices
> While we need to address this craiziness with a new ExtendedType, I suspect 
> if a user sets his Java server TZ to UTC, they should avoid all this 
> insanity. Though of course YMMV when e.g. running unit tests in a specific TZ 
> you may run into this problem.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to