diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java index a60835ae643b..17d55e90340e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcDateJavaType.java @@ -6,11 +6,11 @@ import java.sql.Types; import java.time.LocalDate; -import java.time.LocalTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -114,7 +114,7 @@ public Date coerce(Object value, CoercionContext coercionContext) { return wrap( value, null ); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Object unwrap(Date value, Class type, WrapperOptions options) { if ( value == null ) { @@ -169,8 +169,9 @@ private java.sql.Date unwrapSqlDate(Date value) { final long dateEpoch = toDateEpoch( date.getTime() ); return dateEpoch == date.getTime() ? date : new java.sql.Date( dateEpoch ); } - return new java.sql.Date( unwrapDateEpoch( value ) ); - + else { + return new java.sql.Date( unwrapDateEpoch( value ) ); + } } private static long unwrapDateEpoch(Date value) { @@ -178,7 +179,7 @@ private static long unwrapDateEpoch(Date value) { } private static long toDateEpoch(long value) { - Calendar calendar = Calendar.getInstance(); + final var calendar = Calendar.getInstance(); calendar.setTimeInMillis( value ); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.clear(Calendar.MINUTE); @@ -216,14 +217,15 @@ public Date wrap(Object value, WrapperOptions options) { throw unknownWrap( value.getClass() ); } + private static TemporalAccessor fromDate(Date value) { + return value instanceof java.sql.Date date + ? date.toLocalDate() + : LocalDate.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ); + } + @Override public String toString(Date value) { - if ( value instanceof java.sql.Date ) { - return LITERAL_FORMATTER.format( ( (java.sql.Date) value ).toLocalDate() ); - } - else { - return LITERAL_FORMATTER.format( LocalDate.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ) ); - } + return LITERAL_FORMATTER.format( fromDate( value ) ); } @Override @@ -250,12 +252,7 @@ public Date fromEncodedString(CharSequence charSequence, int start, int end) { @Override public void appendEncodedString(SqlAppender sb, Date value) { - if ( value instanceof java.sql.Date ) { - LITERAL_FORMATTER.formatTo( ( (java.sql.Date) value ).toLocalDate(), sb ); - } - else { - LITERAL_FORMATTER.formatTo( LocalTime.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ), sb ); - } + LITERAL_FORMATTER.formatTo( fromDate( value ), sb ); } @Override @@ -274,12 +271,9 @@ public static class DateMutabilityPlan extends MutableMutabilityPlan { @Override public Date deepCopyNotNull(Date value) { - if ( value instanceof java.sql.Date ) { - return value; - } - else { - return new java.sql.Date( value.getTime() ); - } + return value instanceof java.sql.Date + ? value + : new java.sql.Date( value.getTime() ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java index 0ae67b8a4367..f5dc8d21ea55 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimeJavaType.java @@ -116,7 +116,7 @@ public Date coerce(Object value, CoercionContext coercionContext) { return wrap( value, null ); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Object unwrap(Date value, Class type, WrapperOptions options) { if ( value == null ) { @@ -124,9 +124,10 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { } if ( LocalTime.class.isAssignableFrom( type ) ) { - final Time time = value instanceof java.sql.Time - ? (java.sql.Time) value - : new java.sql.Time( value.getTime() % 86_400_000 ); + final var time = + value instanceof java.sql.Time + ? (java.sql.Time) value + : new java.sql.Time( value.getTime() % 86_400_000 ); final var localTime = time.toLocalTime(); long millis = time.getTime() % 1000; if ( millis == 0 ) { @@ -209,14 +210,15 @@ public Date wrap(Object value, WrapperOptions options) { throw unknownWrap( value.getClass() ); } + private static LocalTime fromDate(Date value) { + return value instanceof Time time + ? time.toLocalTime() + : LocalTime.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ); + } + @Override public String toString(Date value) { - if ( value instanceof java.sql.Time time ) { - return LITERAL_FORMATTER.format( time.toLocalTime() ); - } - else { - return LITERAL_FORMATTER.format( LocalTime.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ) ); - } + return LITERAL_FORMATTER.format( fromDate( value ) ); } @Override @@ -246,12 +248,7 @@ public Date fromEncodedString(CharSequence charSequence, int start, int end) { @Override public void appendEncodedString(SqlAppender sb, Date value) { - if ( value instanceof java.sql.Time time ) { - LITERAL_FORMATTER.formatTo( time.toLocalTime(), sb ); - } - else { - LITERAL_FORMATTER.formatTo( LocalTime.ofInstant( value.toInstant(), ZoneOffset.systemDefault() ), sb ); - } + LITERAL_FORMATTER.formatTo( fromDate( value ), sb ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java index 601b91699397..e18fbc917e1e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JdbcTimestampJavaType.java @@ -6,7 +6,6 @@ import java.sql.Timestamp; import java.sql.Types; -import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; @@ -120,7 +119,7 @@ public Date coerce(Object value, CoercionContext coercionContext) { return wrap( value, null ); } - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Object unwrap(Date value, Class type, WrapperOptions options) { if ( value == null ) { @@ -138,7 +137,7 @@ public Object unwrap(Date value, Class type, WrapperOptions options) { } if ( LocalDateTime.class.isAssignableFrom( type ) ) { - final Instant instant = value.toInstant(); + final var instant = value.toInstant(); return LocalDateTime.ofInstant( instant, ZoneId.systemDefault() ); } @@ -230,15 +229,14 @@ public void appendEncodedString(SqlAppender sb, Date value) { public Date fromEncodedString(CharSequence charSequence, int start, int end) { try { final var temporalAccessor = ENCODED_FORMATTER.parse( subSequence( charSequence, start, end ) ); - final Timestamp timestamp; if ( temporalAccessor.isSupported( ChronoField.INSTANT_SECONDS ) ) { - timestamp = new Timestamp( temporalAccessor.getLong( ChronoField.INSTANT_SECONDS ) * 1000L ); + final var timestamp = new Timestamp( temporalAccessor.getLong( ChronoField.INSTANT_SECONDS ) * 1000L ); timestamp.setNanos( temporalAccessor.get( ChronoField.NANO_OF_SECOND ) ); + return timestamp; } else { - timestamp = Timestamp.valueOf( LocalDateTime.from( temporalAccessor ) ); + return Timestamp.valueOf( LocalDateTime.from( temporalAccessor ) ); } - return timestamp; } catch ( DateTimeParseException pe) { throw new HibernateException( "could not parse timestamp string " + subSequence( charSequence, start, end ), pe ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java index 93658de28f30..949d2e4d940d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateJavaType.java @@ -94,13 +94,11 @@ public X unwrap(LocalDate value, Class type, WrapperOptions options) { final LocalDateTime localDateTime = value.atStartOfDay(); if ( Timestamp.class.isAssignableFrom( type ) ) { - /* - * Workaround for HHH-13266 (JDK-8061577). - * We could have done Timestamp.from( localDateTime.atZone( ZoneId.systemDefault() ).toInstant() ), - * but on top of being more complex than the line below, it 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. - */ + // Workaround for HHH-13266 (JDK-8061577). + // We could have done Timestamp.from( localDateTime.atZone( ZoneId.systemDefault() ).toInstant() ), + // but on top of being more complex than the line below, it 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. return (X) Timestamp.valueOf( localDateTime ); } @@ -134,13 +132,11 @@ public LocalDate wrap(X value, WrapperOptions options) { } if (value instanceof Timestamp timestamp) { - /* - * Workaround for HHH-13266 (JDK-8061577). - * We used to do LocalDateTime.ofInstant( ts.toInstant(), ZoneId.systemDefault() ).toLocalDate(), - * but on top of being more complex than the line below, it 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. - */ + // Workaround for HHH-13266 (JDK-8061577). + // We used to do LocalDateTime.ofInstant( ts.toInstant(), ZoneId.systemDefault() ).toLocalDate(), + // but on top of being more complex than the line below, it 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. return timestamp.toLocalDateTime().toLocalDate(); } @@ -154,12 +150,9 @@ public LocalDate wrap(X value, WrapperOptions options) { } if (value instanceof Date date) { - if (value instanceof java.sql.Date sqlDate) { - return sqlDate.toLocalDate(); - } - else { - return Instant.ofEpochMilli( date.getTime() ).atZone( ZoneId.systemDefault() ).toLocalDate(); - } + return value instanceof java.sql.Date sqlDate + ? sqlDate.toLocalDate() + : Instant.ofEpochMilli( date.getTime() ).atZone( ZoneId.systemDefault() ).toLocalDate(); } throw unknownWrap( value.getClass() ); diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java index 8a4d64752a54..426903e455c3 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalDateTimeJavaType.java @@ -89,13 +89,11 @@ public X unwrap(LocalDateTime value, Class type, WrapperOptions options) } if ( Timestamp.class.isAssignableFrom( type ) ) { - /* - * Workaround for HHH-13266 (JDK-8061577). - * We used to do Timestamp.from( value.atZone( ZoneId.systemDefault() ).toInstant() ), - * but on top of being more complex than the line below, it 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. - */ + // Workaround for HHH-13266 (JDK-8061577). + // We used to do Timestamp.from( value.atZone( ZoneId.systemDefault() ).toInstant() ), + // but on top of being more complex than the line below, it 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. return (X) Timestamp.valueOf( value ); } @@ -137,13 +135,11 @@ public LocalDateTime wrap(X value, WrapperOptions options) { } if (value instanceof Timestamp timestamp) { - /* - * Workaround for HHH-13266 (JDK-8061577). - * We used to do LocalDateTime.ofInstant( ts.toInstant(), ZoneId.systemDefault() ), - * but on top of being more complex than the line below, it 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. - */ + // Workaround for HHH-13266 (JDK-8061577). + // We used to do LocalDateTime.ofInstant( ts.toInstant(), ZoneId.systemDefault() ), + // but on top of being more complex than the line below, it 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. return timestamp.toLocalDateTime(); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java index e7d981fbd723..ac7a498cba6d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LocalTimeJavaType.java @@ -22,12 +22,13 @@ import org.hibernate.dialect.Dialect; import org.hibernate.type.SqlTypes; -import org.hibernate.type.descriptor.DateTimeUtils; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; import org.hibernate.type.spi.TypeConfiguration; +import static org.hibernate.type.descriptor.DateTimeUtils.roundToPrecision; + /** * Java type descriptor for the {@link LocalTime} type. * @@ -94,19 +95,20 @@ public X unwrap(LocalTime value, Class type, WrapperOptions options) { if ( Time.class.isAssignableFrom( type ) ) { final var time = Time.valueOf( value ); - if ( value.getNano() == 0 ) { - return (X) time; - } - // Preserve milliseconds, which java.sql.Time supports - return (X) new Time( time.getTime() + DateTimeUtils.roundToPrecision( value.getNano(), 3 ) / 1000000 ); + final int nanos = value.getNano(); + return nanos == 0 + ? (X) time + // Preserve milliseconds, which java.sql.Time supports + : (X) new Time( time.getTime() + roundToPrecision( nanos, 3 ) / 1000000 ); } // Oracle documentation says to set the Date to January 1, 1970 when convert from - // a LocalTime to a Calendar. IMO the same should hold true for converting to all - // the legacy Date/Time types... - + // a LocalTime to a Calendar. IMO the same should hold true for converting to all + // the legacy Date/Time types. - final var zonedDateTime = value.atDate( LocalDate.of( 1970, 1, 1 ) ).atZone( ZoneId.systemDefault() ); + final var zonedDateTime = + value.atDate( LocalDate.of( 1970, 1, 1 ) ) + .atZone( ZoneId.systemDefault() ); if ( Calendar.class.isAssignableFrom( type ) ) { return (X) GregorianCalendar.from( zonedDateTime ); @@ -140,7 +142,7 @@ public LocalTime wrap(X value, WrapperOptions options) { } if (value instanceof Time time) { - final LocalTime localTime = time.toLocalTime(); + final var localTime = time.toLocalTime(); long millis = time.getTime() % 1000; if ( millis == 0 ) { return localTime; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java index 8e8b36cc1d29..7cbdb112578a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetDateTimeJavaType.java @@ -15,7 +15,6 @@ import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; -import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -75,10 +74,9 @@ public TemporalType getPrecision() { @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators stdIndicators) { - if ( stdIndicators.isPreferJavaTimeJdbcTypesEnabled() ) { - return stdIndicators.getJdbcType( SqlTypes.OFFSET_DATE_TIME ); - } - return stdIndicators.getJdbcType( stdIndicators.getDefaultZonedTimestampSqlType() ); + return stdIndicators.isPreferJavaTimeJdbcTypesEnabled() + ? stdIndicators.getJdbcType( SqlTypes.OFFSET_DATE_TIME ) + : stdIndicators.getJdbcType( stdIndicators.getDefaultZonedTimestampSqlType() ); } @Override @SuppressWarnings("unchecked") @@ -104,17 +102,16 @@ public OffsetDateTime fromString(CharSequence string) { @Override public OffsetDateTime fromEncodedString(CharSequence charSequence, int start, int end) { try { - final TemporalAccessor temporalAccessor = PARSE_FORMATTER.parse( subSequence( charSequence, start, end ) ); - if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) { - return OffsetDateTime.from( temporalAccessor ); - } - else { - // For databases that don't have timezone support, we encode timestamps at UTC, so allow parsing that as well - return LocalDateTime.from( temporalAccessor ).atOffset( ZoneOffset.UTC ); - } + final var temporalAccessor = PARSE_FORMATTER.parse( subSequence( charSequence, start, end ) ); + return temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) + ? OffsetDateTime.from( temporalAccessor ) + // For databases that don't have timezone support, + // we encode timestamps at UTC, so allow parsing + : LocalDateTime.from( temporalAccessor ).atOffset( ZoneOffset.UTC ); } catch ( DateTimeParseException pe) { - throw new HibernateException( "could not parse timestamp string " + subSequence( charSequence, start, end ), pe ); + throw new HibernateException( "could not parse timestamp string " + + subSequence( charSequence, start, end ), pe ); } } @@ -142,19 +139,21 @@ public X unwrap(OffsetDateTime offsetDateTime, Class type, WrapperOptions } if ( Timestamp.class.isAssignableFrom( type ) ) { - /* - * 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. - */ + // 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. if ( offsetDateTime.getYear() < 1905 ) { return (X) Timestamp.valueOf( - offsetDateTime.atZoneSameInstant( ZoneId.systemDefault() ).toLocalDateTime() + offsetDateTime.atZoneSameInstant( ZoneId.systemDefault() ) + .toLocalDateTime() ); } else { @@ -200,22 +199,20 @@ public OffsetDateTime wrap(X value, WrapperOptions options) { } if (value instanceof Timestamp timestamp) { - /* - * 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. - */ - if ( timestamp.getYear() < 5 ) { // Timestamp year 0 is 1900 - return timestamp.toLocalDateTime().atZone( ZoneId.systemDefault() ).toOffsetDateTime(); - } - else { - return OffsetDateTime.ofInstant( timestamp.toInstant(), ZoneId.systemDefault() ); - } + // 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 timestamp.getYear() < 5 // Timestamp year 0 is 1900 + ? timestamp.toLocalDateTime().atZone( ZoneId.systemDefault() ).toOffsetDateTime() + : OffsetDateTime.ofInstant( timestamp.toInstant(), ZoneId.systemDefault() ); } if (value instanceof Date date) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java index 7d32af5f59ab..0c4ec1a3c9dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/OffsetTimeJavaType.java @@ -20,7 +20,6 @@ import org.hibernate.dialect.Dialect; import org.hibernate.type.SqlTypes; -import org.hibernate.type.descriptor.DateTimeUtils; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; @@ -28,6 +27,8 @@ import jakarta.persistence.TemporalType; +import static org.hibernate.type.descriptor.DateTimeUtils.roundToPrecision; + /** * Java type descriptor for the {@link OffsetTime} type. * @@ -55,10 +56,9 @@ public TemporalType getPrecision() { @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators stdIndicators) { - if ( stdIndicators.isPreferJavaTimeJdbcTypesEnabled() ) { - return stdIndicators.getJdbcType( SqlTypes.OFFSET_TIME ); - } - return stdIndicators.getJdbcType( stdIndicators.getDefaultZonedTimeSqlType() ); + return stdIndicators.isPreferJavaTimeJdbcTypesEnabled() + ? stdIndicators.getJdbcType( SqlTypes.OFFSET_TIME ) + : stdIndicators.getJdbcType( stdIndicators.getDefaultZonedTimeSqlType() ); } @Override @@ -107,27 +107,25 @@ public X unwrap(OffsetTime offsetTime, Class type, WrapperOptions options // for legacy types, we assume that the JDBC timezone is passed to JDBC // (since PS.setTime() and friends do accept a timezone passed as a Calendar) - final OffsetTime jdbcOffsetTime = offsetTime.withOffsetSameInstant( getCurrentJdbcOffset(options) ); + final var jdbcOffsetTime = offsetTime.withOffsetSameInstant( getCurrentJdbcOffset(options) ); if ( Time.class.isAssignableFrom( type ) ) { - final Time time = Time.valueOf( jdbcOffsetTime.toLocalTime() ); - if ( jdbcOffsetTime.getNano() == 0 ) { - return (X) time; - } - // Preserve milliseconds, which java.sql.Time supports - return (X) new Time( time.getTime() + DateTimeUtils.roundToPrecision( jdbcOffsetTime.getNano(), 3 ) / 1000000 ); + final var time = Time.valueOf( jdbcOffsetTime.toLocalTime() ); + final int nanos = jdbcOffsetTime.getNano(); + return nanos == 0 + ? (X) time + // Preserve milliseconds, which java.sql.Time supports + : (X) new Time( time.getTime() + roundToPrecision( nanos, 3 ) / 1000000 ); } - final OffsetDateTime jdbcOffsetDateTime = jdbcOffsetTime.atDate( LocalDate.EPOCH ); + final var jdbcOffsetDateTime = jdbcOffsetTime.atDate( LocalDate.EPOCH ); if ( Timestamp.class.isAssignableFrom( type ) ) { - /* - * Workaround for HHH-13266 (JDK-8061577). - * Ideally we'd want to use Timestamp.from( jdbcOffsetDateTime.toInstant() ), - * but this won't always work since 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. - */ + // Workaround for HHH-13266 (JDK-8061577). + // Ideally, we'd want to use Timestamp.from( jdbcOffsetDateTime.toInstant() ), + // but this won't always work since 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. return (X) Timestamp.valueOf( jdbcOffsetDateTime.toLocalDateTime() ); } @@ -171,28 +169,26 @@ public OffsetTime wrap(X value, WrapperOptions options) { return offsetDateTime.toOffsetTime(); } - /* - * Also, in order to fix HHH-13357, and to be consistent with the conversion to Time (see above), - * we set the offset to the current offset of the JVM (OffsetDateTime.now().getOffset()). - * This is different from setting the *zone* to the current *zone* of the JVM (ZoneId.systemDefault()), - * since a zone has a varying offset over time, - * thus the zone might have a different offset for the given timezone than it has for the current date/time. - * For example, if the timestamp represents 1970-01-01TXX:YY, - * and the JVM is set to use Europe/Paris as a timezone, and the current time is 2019-04-16-08:53, - * then applying the JVM timezone to the timestamp would result in the offset +01:00, - * but applying the JVM offset would result in the offset +02:00, since DST is in effect at 2019-04-16-08:53. - * - * Of course none of this would be a problem if we just stored the offset in the database, - * but I guess there are historical reasons that explain why we don't. - */ + // Also, in order to fix HHH-13357, and to be consistent with the conversion to Time (see above), + // we set the offset to the current offset of the JVM (OffsetDateTime.now().getOffset()). This is + // different from setting the *zone* to the current *zone* of the JVM (ZoneId.systemDefault()), + // since a zone has a varying offset over time; thus the zone might have a different offset for the + // given timezone than it has for the current date/time. For example, if the timestamp represents + // 1970-01-01TXX:YY, and the JVM is set to use Europe/Paris as a timezone, and the current time is + // 2019-04-16-08:53, then applying the JVM timezone to the timestamp would result in the offset + // +01:00, but applying the JVM offset would result in the offset +02:00, since DST is in effect at + // 2019-04-16-08:53. + // + // Of course, none of this would be a problem if we just stored the offset in the database, but I + // guess there are historical reasons that explain why we don't. // for legacy types, we assume that the JDBC timezone is passed to JDBC // (since PS.setTime() and friends do accept a timezone passed as a Calendar) if (value instanceof Time time) { - final OffsetTime offsetTime = time.toLocalTime() - .atOffset( getCurrentJdbcOffset( options) ) - .withOffsetSameInstant( getCurrentSystemOffset() ); + final var offsetTime = + time.toLocalTime().atOffset( getCurrentJdbcOffset( options) ) + .withOffsetSameInstant( getCurrentSystemOffset() ); long millis = time.getTime() % 1000; if ( millis == 0 ) { return offsetTime; @@ -206,13 +202,11 @@ public OffsetTime wrap(X value, WrapperOptions options) { } if (value instanceof Timestamp timestamp) { - /* - * Workaround for HHH-13266 (JDK-8061577). - * Ideally we'd want to use OffsetDateTime.ofInstant( ts.toInstant(), ... ), - * but this won't always work since 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. - */ + // Workaround for HHH-13266 (JDK-8061577). + // Ideally, we'd want to use OffsetDateTime.ofInstant( ts.toInstant(), ... ), + // but this won't always work since 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. return timestamp.toLocalDateTime().toLocalTime().atOffset( getCurrentJdbcOffset(options) ) .withOffsetSameInstant( getCurrentSystemOffset() ); } @@ -235,12 +229,10 @@ public OffsetTime wrap(X value, WrapperOptions options) { } private static ZoneOffset getCurrentJdbcOffset(WrapperOptions options) { - if ( options.getJdbcTimeZone() != null ) { - return OffsetDateTime.now().atZoneSameInstant( options.getJdbcTimeZone().toZoneId() ).getOffset(); - } - else { - return getCurrentSystemOffset(); - } + final var jdbcTimeZone = options.getJdbcTimeZone(); + return jdbcTimeZone != null + ? OffsetDateTime.now().atZoneSameInstant( jdbcTimeZone.toZoneId() ).getOffset() + : getCurrentSystemOffset(); } private static ZoneOffset getCurrentSystemOffset() { diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java index 22cde050e664..cfd3b950e58d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ZonedDateTimeJavaType.java @@ -54,10 +54,9 @@ public TemporalType getPrecision() { @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators stdIndicators) { - if ( stdIndicators.isPreferJavaTimeJdbcTypesEnabled() ) { - return stdIndicators.getJdbcType( SqlTypes.ZONED_DATE_TIME ); - } - return stdIndicators.getJdbcType( stdIndicators.getDefaultZonedTimestampSqlType() ); + return stdIndicators.isPreferJavaTimeJdbcTypesEnabled() + ? stdIndicators.getJdbcType( SqlTypes.ZONED_DATE_TIME ) + : stdIndicators.getJdbcType( stdIndicators.getDefaultZonedTimestampSqlType() ); } @Override @SuppressWarnings("unchecked") @@ -104,19 +103,21 @@ public X unwrap(ZonedDateTime zonedDateTime, Class type, WrapperOptions o } if ( Timestamp.class.isAssignableFrom( type ) ) { - /* - * 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. - */ + // 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. if ( zonedDateTime.getYear() < 1905 ) { return (X) Timestamp.valueOf( - zonedDateTime.withZoneSameInstant( ZoneId.systemDefault() ).toLocalDateTime() + zonedDateTime.withZoneSameInstant( ZoneId.systemDefault() ) + .toLocalDateTime() ); } else { @@ -162,22 +163,20 @@ public ZonedDateTime wrap(X value, WrapperOptions options) { } if (value instanceof Timestamp timestamp) { - /* - * 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. - */ - if ( timestamp.getYear() < 5 ) { // Timestamp year 0 is 1900 - return timestamp.toLocalDateTime().atZone( ZoneId.systemDefault() ); - } - else { - return timestamp.toInstant().atZone( ZoneId.systemDefault() ); - } + // 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 timestamp.getYear() < 5 // Timestamp year 0 is 1900 + ? timestamp.toLocalDateTime().atZone( ZoneId.systemDefault() ) + : timestamp.toInstant().atZone( ZoneId.systemDefault() ); } if (value instanceof Date date) {