Skip to content

Commit

Permalink
HHH-13379 Do not change the instant when storing a java.time type rep…
Browse files Browse the repository at this point in the history
…resenting 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.
  • Loading branch information
yrodiere authored and gbadner committed Jul 29, 2019
1 parent de8e965 commit 89d304a
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 30 deletions.
Expand Up @@ -62,12 +62,22 @@ public <X> X unwrap(Instant instant, Class<X> type, WrapperOptions options) {

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 ) ) {
Expand Down Expand Up @@ -102,12 +112,21 @@ public <X> Instant wrap(X value, WrapperOptions options) {
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 ) ) {
Expand Down
Expand Up @@ -60,12 +60,23 @@ public <X> X unwrap(OffsetDateTime offsetDateTime, Class<X> type, WrapperOptions

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 ) ) {
Expand Down Expand Up @@ -100,12 +111,21 @@ public <X> OffsetDateTime wrap(X value, WrapperOptions options) {
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 ) ) {
Expand Down
Expand Up @@ -60,12 +60,23 @@ public <X> X unwrap(ZonedDateTime zonedDateTime, Class<X> type, WrapperOptions o

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 ) ) {
Expand Down Expand Up @@ -100,12 +111,21 @@ public <X> ZonedDateTime wrap(X value, WrapperOptions options) {
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 ) ) {
Expand Down

0 comments on commit 89d304a

Please sign in to comment.