From 7408483a43ce4e6b3ecef04534fb3c2103c25e5f Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 15 Jul 2021 17:14:22 +1200 Subject: [PATCH] #2264 - Use of default timezone for OffsetDateTime offsets problematic for unit tests and presentation layer --- .../server/type/DefaultTypeManager.java | 12 ++++- .../server/type/ScalarTypeOffsetDateTime.java | 7 ++- .../server/type/ScalarTypeZonedDateTime.java | 7 ++- .../type/ScalarTypeOffsetDateTimeTest.java | 50 +++++++++++++++--- .../type/ScalarTypeZonedDateTimeTest.java | 51 ++++++++++++++++--- 5 files changed, 109 insertions(+), 18 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java index 82c3ff46a..befe46b20 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java @@ -750,12 +750,15 @@ public final class DefaultTypeManager implements TypeManager { } private void initialiseJavaTimeTypes(DatabaseConfig config) { + + ZoneId zoneId = getZoneId(config); + typeMap.put(java.nio.file.Path.class, new ScalarTypePath()); addType(java.time.Period.class, new ScalarTypePeriod()); addType(java.time.LocalDate.class, new ScalarTypeLocalDate(jsonDate)); addType(java.time.LocalDateTime.class, new ScalarTypeLocalDateTime(jsonDateTime)); - addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime)); - addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime)); + addType(OffsetDateTime.class, new ScalarTypeOffsetDateTime(jsonDateTime, zoneId)); + addType(ZonedDateTime.class, new ScalarTypeZonedDateTime(jsonDateTime, zoneId)); addType(Instant.class, new ScalarTypeInstant(jsonDateTime)); addType(DayOfWeek.class, new ScalarTypeDayOfWeek()); addType(Month.class, new ScalarTypeMonth()); @@ -771,6 +774,11 @@ public final class DefaultTypeManager implements TypeManager { addType(Duration.class, (durationNanos) ? new ScalarTypeDurationWithNanos() : new ScalarTypeDuration()); } + private ZoneId getZoneId(DatabaseConfig config) { + final String dataTimeZone = config.getDataTimeZone(); + return (dataTimeZone == null) ? ZoneOffset.systemDefault() : TimeZone.getTimeZone(dataTimeZone).toZoneId(); + } + private void addType(Class clazz, ScalarType scalarType) { typeMap.put(clazz, scalarType); logicalMap.putIfAbsent(clazz.getSimpleName(), scalarType); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java index b62c693d3..b68f3eba9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTime.java @@ -15,8 +15,11 @@ import static io.ebeaninternal.server.type.IsoJsonDateTimeParser.formatIso; */ public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime { - public ScalarTypeOffsetDateTime(JsonConfig.DateTime mode) { + private final ZoneId zoneId; + + public ScalarTypeOffsetDateTime(JsonConfig.DateTime mode, ZoneId zoneId) { super(mode, OffsetDateTime.class, false, Types.TIMESTAMP); + this.zoneId = zoneId; } @Override @@ -46,7 +49,7 @@ public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime { - public ScalarTypeZonedDateTime(JsonConfig.DateTime mode) { + private final ZoneId zoneId; + + public ScalarTypeZonedDateTime(JsonConfig.DateTime mode, ZoneId zoneId) { super(mode, ZonedDateTime.class, false, Types.TIMESTAMP); + this.zoneId = zoneId; } @Override @@ -44,7 +47,7 @@ public class ScalarTypeZonedDateTime extends ScalarTypeBaseDateTime jsonTester = new JsonTester<>(type); jsonTester.test(now); - ScalarTypeOffsetDateTime typeNanos = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.NANOS); + ScalarTypeOffsetDateTime typeNanos = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.NANOS, ZoneOffset.systemDefault()); jsonTester = new JsonTester<>(typeNanos); jsonTester.test(now); - ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601); + ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601, ZoneOffset.systemDefault()); jsonTester = new JsonTester<>(typeIso); jsonTester.test(now); } @@ -81,7 +119,7 @@ public class ScalarTypeOffsetDateTimeTest { @Test public void isoJsonFormatParse() { - ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601); + ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601, ZoneOffset.systemDefault()); OffsetDateTime now = OffsetDateTime.now(); String asJson = typeIso.toJsonISO8601(now); diff --git a/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java b/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java index ea501b273..168041723 100644 --- a/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java +++ b/ebean-core/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java @@ -4,7 +4,11 @@ import io.ebean.config.JsonConfig; import org.junit.Test; import java.sql.Timestamp; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.TimeZone; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; @@ -12,12 +16,12 @@ import static org.junit.Assert.*; public class ScalarTypeZonedDateTimeTest { - ScalarTypeZonedDateTime type = new ScalarTypeZonedDateTime(JsonConfig.DateTime.MILLIS); + ScalarTypeZonedDateTime type = new ScalarTypeZonedDateTime(JsonConfig.DateTime.MILLIS, ZoneId.systemDefault()); ZonedDateTime warmUp = ZonedDateTime.now(); @Test - public void testConvertToMillis() throws Exception { + public void testConvertToMillis() { warmUp.hashCode(); @@ -29,7 +33,7 @@ public class ScalarTypeZonedDateTimeTest { } @Test - public void testConvertFromTimestamp() throws Exception { + public void testConvertFromTimestamp() { Timestamp now = new Timestamp(System.currentTimeMillis()); @@ -39,6 +43,41 @@ public class ScalarTypeZonedDateTimeTest { assertEquals(now, timestamp); } + @Test + public void convertFromInstant_with_UTC_expect_matchingZoneOffset() { + final TimeZone timeZoneToUse = TimeZone.getTimeZone("UTC"); + final ZoneOffset expectedZoneOffset = ZoneOffset.UTC; + + convertFromInstantWithConfiguredTimeZone(timeZoneToUse, expectedZoneOffset); + } + + @Test + public void convertFromInstant_with_EST_expect_matchingZoneOffset() { + final TimeZone timeZoneToUse = TimeZone.getTimeZone("EST"); + final ZoneOffset expectedOffset = OffsetDateTime.now(timeZoneToUse.toZoneId()).getOffset(); + + convertFromInstantWithConfiguredTimeZone(timeZoneToUse, expectedOffset); + } + + private void convertFromInstantWithConfiguredTimeZone(TimeZone timeZoneToUse, ZoneOffset expectedZoneOffset) { + TimeZone previous = TimeZone.getDefault(); + try { + OffsetDateTime dateTime = OffsetDateTime.parse("2021-01-01T00:00:00+11:00"); + + // test ScalarTypeOffsetDateTime with the configured timeZone to use + ScalarTypeZonedDateTime type = new ScalarTypeZonedDateTime(JsonConfig.DateTime.MILLIS, timeZoneToUse.toZoneId()); + + // effectively we desire to ignore the system timezone and use the configured one + TimeZone.setDefault(timeZoneToUse); + + final ZonedDateTime zonedDateTime = type.convertFromInstant(dateTime.toInstant()); + + assertEquals(expectedZoneOffset, zonedDateTime.getOffset()); + + } finally { + TimeZone.setDefault(previous); + } + } @Test public void testToJdbcType() throws Exception { @@ -68,11 +107,11 @@ public class ScalarTypeZonedDateTimeTest { JsonTester jsonTester = new JsonTester<>(type); jsonTester.test(now); - ScalarTypeZonedDateTime typeNanos = new ScalarTypeZonedDateTime(JsonConfig.DateTime.NANOS); + ScalarTypeZonedDateTime typeNanos = new ScalarTypeZonedDateTime(JsonConfig.DateTime.NANOS, ZoneId.systemDefault()); jsonTester = new JsonTester<>(typeNanos); jsonTester.test(now); - ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601); + ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601, ZoneId.systemDefault()); jsonTester = new JsonTester<>(typeIso); jsonTester.test(now); } @@ -80,7 +119,7 @@ public class ScalarTypeZonedDateTimeTest { @Test public void toJsonISO8601() { - ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601); + ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601, ZoneId.systemDefault()); ZonedDateTime now = ZonedDateTime.now(); String asJson = typeIso.toJsonISO8601(now);