Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ private static Dialect determineDialect() {
protected static final ZoneId ZONE_GMT = ZoneId.of( "GMT" );
protected static final ZoneId ZONE_OSLO = ZoneId.of( "Europe/Oslo" );
protected static final ZoneId ZONE_AMSTERDAM = ZoneId.of( "Europe/Amsterdam" );
protected static final ZoneId ZONE_AUCKLAND = ZoneId.of( "Pacific/Auckland" );
protected static final ZoneId ZONE_SANTIAGO = ZoneId.of( "America/Santiago" );

private final EnvironmentParameters env;

Expand Down Expand Up @@ -243,6 +245,8 @@ protected static abstract class AbstractParametersBuilder<S extends AbstractPara

private final List<Class<? extends AbstractRemappingH2Dialect>> remappingDialectClasses = new ArrayList<>();

private ZoneId forcedJdbcTimeZone = null;

protected AbstractParametersBuilder() {
dialect = determineDialect();
remappingDialectClasses.add( null ); // Always test without remapping
Expand All @@ -261,6 +265,18 @@ public S skippedForDialects(List<Class<?>> dialectClasses, Consumer<S> skippedIf
return thisAsS();
}

public S withForcedJdbcTimezone(String zoneIdString, Consumer<S> contributor) {
ZoneId zoneId = ZoneId.of( zoneIdString );
this.forcedJdbcTimeZone = zoneId;
try {
contributor.accept( thisAsS() );
}
finally {
this.forcedJdbcTimeZone = null;
}
return thisAsS();
}

@SafeVarargs
public final S alsoTestRemappingsWithH2(Class<? extends AbstractRemappingH2Dialect> ... dialectClasses) {
if ( dialect instanceof H2Dialect ) {
Expand All @@ -281,24 +297,26 @@ protected final S add(ZoneId defaultJvmTimeZone, Object ... subClassParameters)
parameters.add(
new EnvironmentParameters(
defaultJvmTimeZone,
null,
forcedJdbcTimeZone,
remappingDialectClass
)
);
Collections.addAll( parameters, subClassParameters );
result.add( parameters.toArray() );
}
for ( ZoneId hibernateJdbcTimeZone : getHibernateJdbcTimeZonesToTest() ) {
List<Object> parameters = new ArrayList<>();
parameters.add(
new EnvironmentParameters(
defaultJvmTimeZone,
hibernateJdbcTimeZone,
null
)
);
Collections.addAll( parameters, subClassParameters );
result.add( parameters.toArray() );
if ( forcedJdbcTimeZone == null ) {
for ( ZoneId hibernateJdbcTimeZone : getHibernateJdbcTimeZonesToTest() ) {
List<Object> parameters = new ArrayList<>();
parameters.add(
new EnvironmentParameters(
defaultJvmTimeZone,
hibernateJdbcTimeZone,
null
)
);
Collections.addAll( parameters, subClassParameters );
result.add( parameters.toArray() );
}
}
return thisAsS();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public ParametersBuilder add(int year, int month, int day,
@Parameterized.Parameters(name = "{1}-{2}-{3}T{4}:{5}:{6}.{7}Z {0}")
public static List<Object[]> data() {
return new ParametersBuilder()
// Not affected by HHH-13266 (JDK-8061577)
// Not affected by any known bug
.add( 2017, 11, 6, 19, 19, 1, 0, ZONE_UTC_MINUS_8 )
.add( 2017, 11, 6, 19, 19, 1, 0, ZONE_PARIS )
.add( 2017, 11, 6, 19, 19, 1, 500, ZONE_PARIS )
Expand All @@ -66,6 +66,27 @@ public static List<Object[]> data() {
.add( 1899, 12, 31, 23, 59, 59, 999_999_999, ZONE_AMSTERDAM )
.add( 1600, 1, 1, 0, 0, 0, 0, ZONE_AMSTERDAM )
)
// HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621)
// => This used to work correctly in 5.4.1.Final and earlier
.add( 2018, 10, 28, 1, 0, 0, 0, ZONE_PARIS )
.add( 2018, 3, 31, 14, 0, 0, 0, ZONE_AUCKLAND )
// => This has never worked correctly, unless the JDBC timezone was set to UTC
.withForcedJdbcTimezone( "UTC", b -> b
.add( 2018, 10, 28, 0, 0, 0, 0, ZONE_PARIS )
.add( 2018, 3, 31, 13, 0, 0, 0, ZONE_AUCKLAND )
)
// => Also test DST start, just in case
.add( 2018, 3, 25, 1, 0, 0, 0, ZONE_PARIS )
.add( 2018, 3, 25, 2, 0, 0, 0, ZONE_PARIS )
.add( 2018, 9, 30, 2, 0, 0, 0, ZONE_AUCKLAND )
.add( 2018, 9, 30, 3, 0, 0, 0, ZONE_AUCKLAND )
// => Also test dates around 1905-01-01, because the code behaves differently before and after 1905
.add( 1904, 12, 31, 22, 59, 59, 999_999_999, ZONE_PARIS )
.add( 1904, 12, 31, 23, 59, 59, 999_999_999, ZONE_PARIS )
.add( 1905, 1, 1, 0, 59, 59, 999_999_999, ZONE_PARIS )
.add( 1904, 12, 31, 23, 0, 0, 0, ZONE_PARIS )
.add( 1905, 1, 1, 0, 0, 0, 0, ZONE_PARIS )
.add( 1905, 1, 1, 1, 0, 0, 0, ZONE_PARIS )
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ public static List<Object[]> data() {
.add( 1900, 1, 1, ZONE_AMSTERDAM )
.add( 1600, 1, 1, ZONE_AMSTERDAM )
)
// HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621)
// It doesn't seem that any date at midnight can be affected by HHH-13379, but we add some tests just in case
// => Test the day of DST end
.add( 2018, 10, 28, ZONE_PARIS )
.add( 2018, 9, 30, ZONE_AUCKLAND )
.add( 2018, 5, 13, ZONE_SANTIAGO ) // DST end: 00:00 => 23:00 previous day
// => Also test the day of DST start
.add( 2018, 3, 25, ZONE_PARIS )
.add( 2018, 9, 30, ZONE_AUCKLAND )
.add( 2018, 8, 12, ZONE_SANTIAGO ) // DST start: 00:00 => 01:00
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,23 @@ public static List<Object[]> data() {
.add( 1900, 1, 1, 0, 19, 31, 0, ZONE_AMSTERDAM )
.add( 1600, 1, 1, 0, 0, 0, 0, ZONE_AMSTERDAM )
)
// HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621)
// It doesn't seem that any LocalDateTime can be affected by HHH-13379, but we add some tests just in case
.add( 2018, 10, 28, 1, 0, 0, 0, ZONE_PARIS )
.add( 2018, 10, 28, 2, 0, 0, 0, ZONE_PARIS )
.add( 2018, 10, 28, 3, 0, 0, 0, ZONE_PARIS )
.add( 2018, 10, 28, 4, 0, 0, 0, ZONE_PARIS )
.add( 2018, 4, 1, 1, 0, 0, 0, ZONE_AUCKLAND )
.add( 2018, 4, 1, 2, 0, 0, 0, ZONE_AUCKLAND )
.add( 2018, 4, 1, 3, 0, 0, 0, ZONE_AUCKLAND )
.add( 2018, 4, 1, 4, 0, 0, 0, ZONE_AUCKLAND )
// => Also test DST start
// This does not work, but it's unrelated to HHH-13379; see HHH-13515
//.add( 2018, 3, 25, 2, 0, 0, 0, ZONE_PARIS )
.add( 2018, 3, 25, 3, 0, 0, 0, ZONE_PARIS )
// This does not work, but it's unrelated to HHH-13379; see HHH-13515
//.add( 2018, 9, 30, 2, 0, 0, 0, ZONE_AUCKLAND )
.add( 2018, 9, 30, 3, 0, 0, 0, ZONE_AUCKLAND )
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ public static List<Object[]> data() {
.addPersistedWithoutHibernate( 1900, 1, 1, 0, 19, 31, 0, ZONE_AMSTERDAM )
.addPersistedWithoutHibernate( 1600, 1, 1, 0, 0, 0, 0, ZONE_AMSTERDAM )
)
// HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621)
// It doesn't seem that any time on 1970-01-01 can be affected by HHH-13379, but we add some tests just in case
.add( 1, 0, 0, 0, ZONE_PARIS )
.add( 2, 0, 0, 0, ZONE_PARIS )
.add( 3, 0, 0, 0, ZONE_PARIS )
.add( 1, 0, 0, 0, ZONE_AUCKLAND )
.add( 2, 0, 0, 0, ZONE_AUCKLAND )
.add( 3, 0, 0, 0, ZONE_AUCKLAND )
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public ParametersBuilder add(int year, int month, int day,
@Parameterized.Parameters(name = "{1}-{2}-{3}T{4}:{5}:{6}.{7}[{8}] {0}")
public static List<Object[]> data() {
return new ParametersBuilder()
// Not affected by HHH-13266
// Not affected by any known bug
.add( 2017, 11, 6, 19, 19, 1, 0, "+10:00", ZONE_UTC_MINUS_8 )
.add( 2017, 11, 6, 19, 19, 1, 0, "+07:00", ZONE_UTC_MINUS_8 )
.add( 2017, 11, 6, 19, 19, 1, 0, "+01:30", ZONE_UTC_MINUS_8 )
Expand All @@ -75,6 +75,7 @@ public static List<Object[]> data() {
// MySQL/Mariadb cannot store values equal to epoch exactly, or less, in a timestamp.
Arrays.asList( MySQLDialect.class, MariaDBDialect.class ),
b -> b
// Not affected by any known bug
.add( 1970, 1, 1, 0, 0, 0, 0, "+01:00", ZONE_GMT )
.add( 1970, 1, 1, 0, 0, 0, 0, "+00:00", ZONE_GMT )
.add( 1970, 1, 1, 0, 0, 0, 0, "-01:00", ZONE_GMT )
Expand All @@ -85,13 +86,34 @@ public static List<Object[]> data() {
.add( 1900, 1, 1, 0, 9, 21, 0, "+00:09:21", ZONE_PARIS )
.add( 1900, 1, 1, 0, 19, 32, 0, "+00:19:32", ZONE_PARIS )
.add( 1900, 1, 1, 0, 19, 32, 0, "+00:19:32", ZONE_AMSTERDAM )
// Affected by HHH-13266
// Affected by HHH-13266 (JDK-8061577)
.add( 1892, 1, 1, 0, 0, 0, 0, "+00:00", ZONE_OSLO )
.add( 1900, 1, 1, 0, 9, 20, 0, "+00:09:21", ZONE_PARIS )
.add( 1900, 1, 1, 0, 19, 31, 0, "+00:19:32", ZONE_PARIS )
.add( 1900, 1, 1, 0, 19, 31, 0, "+00:19:32", ZONE_AMSTERDAM )
.add( 1600, 1, 1, 0, 0, 0, 0, "+00:19:32", ZONE_AMSTERDAM )
)
// HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621)
// => This used to work correctly in 5.4.1.Final and earlier
.add( 2018, 10, 28, 2, 0, 0, 0, "+01:00", ZONE_PARIS )
.add( 2018, 4, 1, 2, 0, 0, 0, "+12:00", ZONE_AUCKLAND )
// => This has never worked correctly, unless the JDBC timezone was set to UTC
.withForcedJdbcTimezone( "UTC", b -> b
.add( 2018, 10, 28, 2, 0, 0, 0, "+02:00", ZONE_PARIS )
.add( 2018, 4, 1, 2, 0, 0, 0, "+13:00", ZONE_AUCKLAND )
)
// => Also test DST start, just in case
.add( 2018, 3, 25, 2, 0, 0, 0, "+01:00", ZONE_PARIS )
.add( 2018, 3, 25, 3, 0, 0, 0, "+02:00", ZONE_PARIS )
.add( 2018, 9, 30, 2, 0, 0, 0, "+12:00", ZONE_AUCKLAND )
.add( 2018, 9, 30, 3, 0, 0, 0, "+13:00", ZONE_AUCKLAND )
// => Also test dates around 1905-01-01, because the code behaves differently before and after 1905
.add( 1904, 12, 31, 23, 59, 59, 999_999_999, "-01:00", ZONE_PARIS )
.add( 1904, 12, 31, 23, 59, 59, 999_999_999, "+00:00", ZONE_PARIS )
.add( 1904, 12, 31, 23, 59, 59, 999_999_999, "+01:00", ZONE_PARIS )
.add( 1905, 1, 1, 0, 0, 0, 0, "-01:00", ZONE_PARIS )
.add( 1905, 1, 1, 0, 0, 0, 0, "+00:00", ZONE_PARIS )
.add( 1905, 1, 1, 0, 0, 0, 0, "+01:00", ZONE_PARIS )
.build();
}

Expand Down
Loading