HHH-13379 Do not change the instant when storing a java.time type representing an instant at DST end in the default JVM timezone

Note problems can still occur in the JDBC driver if the JDBC timezone is
not forced to GMT/UTC.
This commit is contained in:
Yoann Rodière 2019-07-26 13:30:13 +02:00 committed by gbadner
parent de8e965cc8
commit 89d304a00e
3 changed files with 89 additions and 30 deletions

View File

@ -62,12 +62,22 @@ public class InstantJavaDescriptor extends AbstractTypeDescriptor<Instant> {
if ( java.sql.Timestamp.class.isAssignableFrom( type ) ) {
/*
* Workaround for HHH-13266 (JDK-8061577).
* Ideally we'd want to use Timestamp.from(), but this won't always work.
* Timestamp.from() assumes the number of milliseconds since the epoch
* means the same thing in Timestamp and Instant, but it doesn't, in particular before 1900.
* This works around two bugs:
* - HHH-13266 (JDK-8061577): around and before 1900,
* the number of milliseconds since the epoch does not mean the same thing
* for java.util and java.time, so conversion must be done using the year, month, day, hour, etc.
* - HHH-13379 (JDK-4312621): after 1908 (approximately),
* Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc representation once a year
* (on DST end), so conversion must be done using the number of milliseconds since the epoch.
* - around 1905, both methods are equally valid, so we don't really care which one is used.
*/
return (X) Timestamp.valueOf( instant.atZone( ZoneId.systemDefault() ).toLocalDateTime() );
ZonedDateTime zonedDateTime = instant.atZone( ZoneId.systemDefault() );
if ( zonedDateTime.getYear() < 1905 ) {
return (X) Timestamp.valueOf( zonedDateTime.toLocalDateTime() );
}
else {
return (X) Timestamp.from( instant );
}
}
if ( java.sql.Date.class.isAssignableFrom( type ) ) {
@ -102,12 +112,21 @@ public class InstantJavaDescriptor extends AbstractTypeDescriptor<Instant> {
if ( Timestamp.class.isInstance( value ) ) {
final Timestamp ts = (Timestamp) value;
/*
* Workaround for HHH-13266 (JDK-8061577).
* Ideally we'd want to use ts.toInstant(), but this won't always work.
* ts.toInstant() assumes the number of milliseconds since the epoch
* means the same thing in Timestamp and Instant, but it doesn't, in particular before 1900.
* This works around two bugs:
* - HHH-13266 (JDK-8061577): around and before 1900,
* the number of milliseconds since the epoch does not mean the same thing
* for java.util and java.time, so conversion must be done using the year, month, day, hour, etc.
* - HHH-13379 (JDK-4312621): after 1908 (approximately),
* Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc representation once a year
* (on DST end), so conversion must be done using the number of milliseconds since the epoch.
* - around 1905, both methods are equally valid, so we don't really care which one is used.
*/
return ts.toLocalDateTime().atZone( ZoneId.systemDefault() ).toInstant();
if ( ts.getYear() < 5 ) { // Timestamp year 0 is 1900
return ts.toLocalDateTime().atZone( ZoneId.systemDefault() ).toInstant();
}
else {
return ts.toInstant();
}
}
if ( Long.class.isInstance( value ) ) {

View File

@ -60,12 +60,23 @@ public class OffsetDateTimeJavaDescriptor extends AbstractTypeDescriptor<OffsetD
if ( Timestamp.class.isAssignableFrom( type ) ) {
/*
* Workaround for HHH-13266 (JDK-8061577).
* Ideally we'd want to use Timestamp.from( offsetDateTime.toInstant() ), but this won't always work.
* Timestamp.from() assumes the number of milliseconds since the epoch
* means the same thing in Timestamp and Instant, but it doesn't, in particular before 1900.
* This works around two bugs:
* - HHH-13266 (JDK-8061577): around and before 1900,
* the number of milliseconds since the epoch does not mean the same thing
* for java.util and java.time, so conversion must be done using the year, month, day, hour, etc.
* - HHH-13379 (JDK-4312621): after 1908 (approximately),
* Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc representation once a year
* (on DST end), so conversion must be done using the number of milliseconds since the epoch.
* - around 1905, both methods are equally valid, so we don't really care which one is used.
*/
return (X) Timestamp.valueOf( offsetDateTime.atZoneSameInstant( ZoneId.systemDefault() ).toLocalDateTime() );
if ( offsetDateTime.getYear() < 1905 ) {
return (X) Timestamp.valueOf(
offsetDateTime.atZoneSameInstant( ZoneId.systemDefault() ).toLocalDateTime()
);
}
else {
return (X) Timestamp.from( offsetDateTime.toInstant() );
}
}
if ( java.sql.Date.class.isAssignableFrom( type ) ) {
@ -100,12 +111,21 @@ public class OffsetDateTimeJavaDescriptor extends AbstractTypeDescriptor<OffsetD
if ( Timestamp.class.isInstance( value ) ) {
final Timestamp ts = (Timestamp) value;
/*
* Workaround for HHH-13266 (JDK-8061577).
* Ideally we'd want to use OffsetDateTime.ofInstant( ts.toInstant(), ... ), but this won't always work.
* ts.toInstant() assumes the number of milliseconds since the epoch
* means the same thing in Timestamp and Instant, but it doesn't, in particular before 1900.
* This works around two bugs:
* - HHH-13266 (JDK-8061577): around and before 1900,
* the number of milliseconds since the epoch does not mean the same thing
* for java.util and java.time, so conversion must be done using the year, month, day, hour, etc.
* - HHH-13379 (JDK-4312621): after 1908 (approximately),
* Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc representation once a year
* (on DST end), so conversion must be done using the number of milliseconds since the epoch.
* - around 1905, both methods are equally valid, so we don't really care which one is used.
*/
return ts.toLocalDateTime().atZone( ZoneId.systemDefault() ).toOffsetDateTime();
if ( ts.getYear() < 5 ) { // Timestamp year 0 is 1900
return ts.toLocalDateTime().atZone( ZoneId.systemDefault() ).toOffsetDateTime();
}
else {
return OffsetDateTime.ofInstant( ts.toInstant(), ZoneId.systemDefault() );
}
}
if ( Date.class.isInstance( value ) ) {

View File

@ -60,12 +60,23 @@ public class ZonedDateTimeJavaDescriptor extends AbstractTypeDescriptor<ZonedDat
if ( Timestamp.class.isAssignableFrom( type ) ) {
/*
* Workaround for HHH-13266 (JDK-8061577).
* Ideally we'd want to use Timestamp.from( zonedDateTime.toInstant() ), but this won't always work.
* Timestamp.from() assumes the number of milliseconds since the epoch
* means the same thing in Timestamp and Instant, but it doesn't, in particular before 1900.
* This works around two bugs:
* - HHH-13266 (JDK-8061577): around and before 1900,
* the number of milliseconds since the epoch does not mean the same thing
* for java.util and java.time, so conversion must be done using the year, month, day, hour, etc.
* - HHH-13379 (JDK-4312621): after 1908 (approximately),
* Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc representation once a year
* (on DST end), so conversion must be done using the number of milliseconds since the epoch.
* - around 1905, both methods are equally valid, so we don't really care which one is used.
*/
return (X) Timestamp.valueOf( zonedDateTime.withZoneSameInstant( ZoneId.systemDefault() ).toLocalDateTime() );
if ( zonedDateTime.getYear() < 1905 ) {
return (X) Timestamp.valueOf(
zonedDateTime.withZoneSameInstant( ZoneId.systemDefault() ).toLocalDateTime()
);
}
else {
return (X) Timestamp.from( zonedDateTime.toInstant() );
}
}
if ( java.sql.Date.class.isAssignableFrom( type ) ) {
@ -100,12 +111,21 @@ public class ZonedDateTimeJavaDescriptor extends AbstractTypeDescriptor<ZonedDat
if ( java.sql.Timestamp.class.isInstance( value ) ) {
final Timestamp ts = (Timestamp) value;
/*
* Workaround for HHH-13266 (JDK-8061577).
* Ideally we'd want to use ZonedDateTime.ofInstant( ts.toInstant(), ... ), but this won't always work.
* ts.toInstant() assumes the number of milliseconds since the epoch
* means the same thing in Timestamp and Instant, but it doesn't, in particular before 1900.
* This works around two bugs:
* - HHH-13266 (JDK-8061577): around and before 1900,
* the number of milliseconds since the epoch does not mean the same thing
* for java.util and java.time, so conversion must be done using the year, month, day, hour, etc.
* - HHH-13379 (JDK-4312621): after 1908 (approximately),
* Daylight Saving Time introduces ambiguity in the year/month/day/hour/etc representation once a year
* (on DST end), so conversion must be done using the number of milliseconds since the epoch.
* - around 1905, both methods are equally valid, so we don't really care which one is used.
*/
return ts.toLocalDateTime().atZone( ZoneId.systemDefault() );
if ( ts.getYear() < 5 ) { // Timestamp year 0 is 1900
return ts.toLocalDateTime().atZone( ZoneId.systemDefault() );
}
else {
return ts.toInstant().atZone( ZoneId.systemDefault() );
}
}
if ( java.util.Date.class.isInstance( value ) ) {