diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaDescriptor.java index db84b82ac3e8..9bc9a52e0a03 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/InstantJavaDescriptor.java @@ -62,12 +62,22 @@ public X unwrap(Instant instant, Class 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 ) ) { @@ -102,12 +112,21 @@ public 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 ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaDescriptor.java index 61f0ccc4766e..c9f624f1050e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaDescriptor.java @@ -60,12 +60,23 @@ public X unwrap(OffsetDateTime offsetDateTime, Class 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 ) ) { @@ -100,12 +111,21 @@ public 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 ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaDescriptor.java index fafb2e25262a..a10bb47a8ecb 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaDescriptor.java @@ -60,12 +60,23 @@ public X unwrap(ZonedDateTime zonedDateTime, Class 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 ) ) { @@ -100,12 +111,21 @@ public 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 ) ) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java index 1c83b62cc4e6..69384e16751e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java @@ -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; @@ -243,6 +245,8 @@ protected static abstract class AbstractParametersBuilder> remappingDialectClasses = new ArrayList<>(); + private ZoneId forcedJdbcTimeZone = null; + protected AbstractParametersBuilder() { dialect = determineDialect(); remappingDialectClasses.add( null ); // Always test without remapping @@ -261,6 +265,18 @@ public S skippedForDialects(List> dialectClasses, Consumer skippedIf return thisAsS(); } + public S withForcedJdbcTimezone(String zoneIdString, Consumer 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 ... dialectClasses) { if ( dialect instanceof H2Dialect ) { @@ -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 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 parameters = new ArrayList<>(); + parameters.add( + new EnvironmentParameters( + defaultJvmTimeZone, + hibernateJdbcTimeZone, + null + ) + ); + Collections.addAll( parameters, subClassParameters ); + result.add( parameters.toArray() ); + } } return thisAsS(); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/InstantTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/InstantTest.java index b88e8782b916..2b960ae66739 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/InstantTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/InstantTest.java @@ -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 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 ) @@ -66,6 +66,27 @@ public static List 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(); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTest.java index 8cf5dabc4ca2..50a00f62c221 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTest.java @@ -65,6 +65,16 @@ public static List 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(); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTimeTest.java index 7224460f4b82..d61cd590c455 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTimeTest.java @@ -61,6 +61,23 @@ public static List 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(); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java index a7aa5628be8f..9c80a3eba1b3 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java @@ -82,6 +82,14 @@ public static List 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(); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/OffsetDateTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/OffsetDateTimeTest.java index 9926fea2f515..dab40cdc92c5 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/OffsetDateTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/OffsetDateTimeTest.java @@ -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 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 ) @@ -75,6 +75,7 @@ public static List 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 ) @@ -85,13 +86,34 @@ public static List 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(); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java index 8a4203cbfc16..e7a2066f95a4 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java @@ -90,6 +90,32 @@ public static List data() { .addPersistedWithoutHibernate( 1900, 1, 1, 0, 19, 31, 0, "+00:19:32", ZONE_AMSTERDAM ) .addPersistedWithoutHibernate( 1600, 1, 1, 0, 0, 0, 0, "+00:19:32", 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, "-01:00", ZONE_PARIS ) + .add( 1, 0, 0, 0, "+00:00", ZONE_PARIS ) + .add( 1, 0, 0, 0, "+01:00", ZONE_PARIS ) + .add( 1, 0, 0, 0, "+02:00", ZONE_PARIS ) + .add( 2, 0, 0, 0, "-01:00", ZONE_PARIS ) + .add( 2, 0, 0, 0, "+00:00", ZONE_PARIS ) + .add( 2, 0, 0, 0, "+01:00", ZONE_PARIS ) + .add( 2, 0, 0, 0, "+02:00", ZONE_PARIS ) + .add( 3, 0, 0, 0, "-01:00", ZONE_PARIS ) + .add( 3, 0, 0, 0, "+00:00", ZONE_PARIS ) + .add( 3, 0, 0, 0, "+01:00", ZONE_PARIS ) + .add( 3, 0, 0, 0, "+02:00", ZONE_PARIS ) + .add( 1, 0, 0, 0, "-01:00", ZONE_AUCKLAND ) + .add( 1, 0, 0, 0, "+00:00", ZONE_AUCKLAND ) + .add( 1, 0, 0, 0, "+01:00", ZONE_AUCKLAND ) + .add( 1, 0, 0, 0, "+02:00", ZONE_AUCKLAND ) + .add( 2, 0, 0, 0, "-01:00", ZONE_AUCKLAND ) + .add( 2, 0, 0, 0, "+00:00", ZONE_AUCKLAND ) + .add( 2, 0, 0, 0, "+01:00", ZONE_AUCKLAND ) + .add( 2, 0, 0, 0, "+02:00", ZONE_AUCKLAND ) + .add( 3, 0, 0, 0, "-01:00", ZONE_AUCKLAND ) + .add( 3, 0, 0, 0, "+00:00", ZONE_AUCKLAND ) + .add( 3, 0, 0, 0, "+01:00", ZONE_AUCKLAND ) + .add( 3, 0, 0, 0, "+02:00", ZONE_AUCKLAND ) .build(); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/ZonedDateTimeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/ZonedDateTimeTest.java index de7fe39ae080..61dad123e915 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/ZonedDateTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/ZonedDateTimeTest.java @@ -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 data() { return new ParametersBuilder() - // Not affected by HHH-13266 + // Not affected by any known bug .add( 2017, 11, 6, 19, 19, 1, 0, "GMT+10:00", ZONE_UTC_MINUS_8 ) .add( 2017, 11, 6, 19, 19, 1, 0, "GMT+07:00", ZONE_UTC_MINUS_8 ) .add( 2017, 11, 6, 19, 19, 1, 0, "GMT+01:30", ZONE_UTC_MINUS_8 ) @@ -79,6 +79,7 @@ public static List 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, "GMT+01:00", ZONE_GMT ) .add( 1970, 1, 1, 0, 0, 0, 0, "GMT+00:00", ZONE_GMT ) .add( 1970, 1, 1, 0, 0, 0, 0, "GMT-01:00", ZONE_GMT ) @@ -93,7 +94,7 @@ public static List data() { .add( 1900, 1, 1, 0, 19, 32, 0, "Europe/Amsterdam", ZONE_PARIS ) .add( 1900, 1, 1, 0, 19, 32, 0, "GMT+00:19:32", ZONE_AMSTERDAM ) .add( 1900, 1, 1, 0, 19, 32, 0, "Europe/Amsterdam", ZONE_AMSTERDAM ) - // Affected by HHH-13266 + // Affected by HHH-13266 (JDK-8061577) .add( 1892, 1, 1, 0, 0, 0, 0, "GMT+00:00", ZONE_OSLO ) .add( 1892, 1, 1, 0, 0, 0, 0, "Europe/Oslo", ZONE_OSLO ) .add( 1900, 1, 1, 0, 9, 20, 0, "GMT+00:09:21", ZONE_PARIS ) @@ -104,6 +105,27 @@ public static List data() { .add( 1600, 1, 1, 0, 0, 0, 0, "GMT+00:19:32", ZONE_AMSTERDAM ) .add( 1600, 1, 1, 0, 0, 0, 0, "Europe/Amsterdam", 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(); }