diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToOffsetDateTimeConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToOffsetDateTimeConverter.java new file mode 100644 index 000000000..768767646 --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToOffsetDateTimeConverter.java @@ -0,0 +1,63 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.core.dialect; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; + +import org.h2.api.TimestampWithTimeZone; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.convert.ReadingConverter; + +/** + * Converter converting from an H2 internal representation of a timestamp with time zone to an OffsetDateTime. + * + * Only required for H2 versions < 2.0 + * + * @author Jens Schauder + * @since 2.7 + */ +@ReadingConverter +public enum H2TimestampWithTimeZoneToOffsetDateTimeConverter + implements Converter { + + INSTANCE; + + @Override + public OffsetDateTime convert(TimestampWithTimeZone source) { + + long nanosInSecond = 1_000_000_000; + long nanosInMinute = nanosInSecond * 60; + long nanosInHour = nanosInMinute * 60; + + long hours = (source.getNanosSinceMidnight() / nanosInHour); + + long nanosInHours = hours * nanosInHour; + long nanosLeft = source.getNanosSinceMidnight() - nanosInHours; + long minutes = nanosLeft / nanosInMinute; + + long nanosInMinutes = minutes * nanosInMinute; + nanosLeft -= nanosInMinutes; + long seconds = nanosLeft / nanosInSecond; + + long nanosInSeconds = seconds * nanosInSecond; + nanosLeft -= nanosInSeconds; + ZoneOffset offset = ZoneOffset.ofTotalSeconds(source.getTimeZoneOffsetSeconds()); + + return OffsetDateTime.of(source.getYear(), source.getMonth(), source.getDay(), (int) hours, (int) minutes, + (int) seconds, (int) nanosLeft, offset); + } +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java index e95ae7741..15807ff4e 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java @@ -15,15 +15,10 @@ */ package org.springframework.data.jdbc.core.dialect; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import org.h2.api.TimestampWithTimeZone; -import org.springframework.core.convert.converter.Converter; -import org.springframework.data.convert.ReadingConverter; import org.springframework.data.relational.core.dialect.Db2Dialect; import org.springframework.data.relational.core.dialect.H2Dialect; @@ -43,39 +38,26 @@ public class JdbcH2Dialect extends H2Dialect { @Override public Collection getConverters() { - List converters = new ArrayList<>(super.getConverters()); - converters.add(TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE); - return converters; - } - - @ReadingConverter - enum TimestampWithTimeZoneToOffsetDateTimeConverter implements Converter { - - INSTANCE; + final Collection originalConverters = super.getConverters(); - @Override - public OffsetDateTime convert(TimestampWithTimeZone source) { + if (isH2belowVersion2()) { - long nanosInSecond = 1_000_000_000; - long nanosInMinute = nanosInSecond * 60; - long nanosInHour = nanosInMinute * 60; - - long hours = (source.getNanosSinceMidnight() / nanosInHour); + List converters = new ArrayList<>(originalConverters); + converters.add(H2TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE); + return converters; + } - long nanosInHours = hours * nanosInHour; - long nanosLeft = source.getNanosSinceMidnight() - nanosInHours; - long minutes = nanosLeft / nanosInMinute; + return originalConverters; + } - long nanosInMinutes = minutes * nanosInMinute; - nanosLeft -= nanosInMinutes; - long seconds = nanosLeft / nanosInSecond; + static boolean isH2belowVersion2() { - long nanosInSeconds = seconds * nanosInSecond; - nanosLeft -= nanosInSeconds; - ZoneOffset offset = ZoneOffset.ofTotalSeconds(source.getTimeZoneOffsetSeconds()); + try { - return OffsetDateTime.of(source.getYear(), source.getMonth(), source.getDay(), (int) hours, (int) minutes, - (int) seconds, (int) nanosLeft, offset); + JdbcH2Dialect.class.getClassLoader().loadClass("org.h2.api.TimestampWithTimeZone"); + return true; + } catch (ClassNotFoundException e) { + return false; } } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcH2DialectTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcH2DialectTests.java index 511ecd3e2..a971d31b5 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcH2DialectTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcH2DialectTests.java @@ -33,7 +33,7 @@ class JdbcH2DialectTests { @Test void TimestampWithTimeZone2OffsetDateTimeConverterConvertsProperly() { - JdbcH2Dialect.TimestampWithTimeZoneToOffsetDateTimeConverter converter = JdbcH2Dialect.TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE; + H2TimestampWithTimeZoneToOffsetDateTimeConverter converter = H2TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE; long dateValue = 123456789; long timeNanos = 987654321; int timeZoneOffsetSeconds = 4 * 60 * 60;