diff --git a/src/main/java/io/ebeaninternal/server/type/IsoJsonDateTimeParser.java b/src/main/java/io/ebeaninternal/server/type/IsoJsonDateTimeParser.java new file mode 100644 index 000000000..a6f8a8636 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/type/IsoJsonDateTimeParser.java @@ -0,0 +1,21 @@ +package io.ebeaninternal.server.type; + +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; + +class IsoJsonDateTimeParser { + + private static final DateTimeFormatter ISO_MILLIS = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .appendInstant(3) + .toFormatter(); + + static Instant parseIso(String jsonDateTime) { + return Instant.parse(jsonDateTime); + } + + static String formatIso(Instant value) { + return ISO_MILLIS.format(value); + } +} diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeBaseDateTime.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeBaseDateTime.java index 17f7c320b..c89632ab7 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeBaseDateTime.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeBaseDateTime.java @@ -1,9 +1,9 @@ package io.ebeaninternal.server.type; -import io.ebean.config.JsonConfig; -import io.ebeanservice.docstore.api.mapping.DocPropertyType; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; +import io.ebean.config.JsonConfig; +import io.ebeanservice.docstore.api.mapping.DocPropertyType; import java.io.DataInput; import java.io.DataOutput; @@ -12,15 +12,15 @@ import java.math.BigDecimal; import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; +import java.time.Instant; + +import static io.ebeaninternal.server.type.IsoJsonDateTimeParser.parseIso; /** * Base type for DateTime types. */ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { - - protected final UtilDateTimeParser dateTimeParser = new UtilDateTimeParser(); - protected final JsonConfig.DateTime mode; public ScalarTypeBaseDateTime(JsonConfig.DateTime mode, Class type, boolean jdbcNative, int jdbcType) { @@ -43,6 +43,11 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { */ public abstract T convertFromTimestamp(Timestamp ts); + /** + * Convert to the value from a Instant. + */ + public abstract T convertFromInstant(Instant ts); + /** * Convert from epoch millis to the value. */ @@ -64,6 +69,13 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { */ protected abstract String toJsonISO8601(T value); + /** + * Convert the value to ISO8601 format. + */ + protected T fromJsonISO8601(String value) { + return convertFromInstant(parseIso(value)); + } + @Override public void bind(DataBind b, T value) throws SQLException { if (value == null) { @@ -104,8 +116,7 @@ public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { return convertFromTimestamp(timestamp); } default: { - String jsonDateTime = parser.getText(); - return convertFromTimestamp(dateTimeParser.parse(jsonDateTime)); + return fromJsonISO8601(parser.getText()); } } } diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeCalendar.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeCalendar.java index 056794583..519a66efb 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeCalendar.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeCalendar.java @@ -7,8 +7,11 @@ import java.sql.Date; import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; +import java.time.Instant; import java.util.Calendar; +import static io.ebeaninternal.server.type.IsoJsonDateTimeParser.formatIso; + /** * ScalarType for java.util.Calendar. */ @@ -48,6 +51,13 @@ public class ScalarTypeCalendar extends ScalarTypeBaseDateTime { return calendar; } + @Override + public Calendar convertFromInstant(Instant ts) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(ts.toEpochMilli()); + return calendar; + } + @Override protected String toJsonNanos(Calendar value) { return String.valueOf(value.getTime()); @@ -55,7 +65,7 @@ public class ScalarTypeCalendar extends ScalarTypeBaseDateTime { @Override protected String toJsonISO8601(Calendar value) { - return dateTimeParser.format(value.getTime()); + return formatIso(value.toInstant()); } @Override diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeInstant.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeInstant.java index 3b98929ce..dd78f4a65 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeInstant.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeInstant.java @@ -25,6 +25,11 @@ public class ScalarTypeInstant extends ScalarTypeBaseDateTime { return value.toString(); } + @Override + protected Instant fromJsonISO8601(String value) { + return Instant.parse(value); + } + @Override public long convertToMillis(Instant value) { return value.toEpochMilli(); @@ -40,6 +45,11 @@ public class ScalarTypeInstant extends ScalarTypeBaseDateTime { return ts.toInstant(); } + @Override + public Instant convertFromInstant(Instant ts) { + return ts; + } + @Override public Timestamp convertToTimestamp(Instant t) { return Timestamp.from(t); diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJodaDateTime.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJodaDateTime.java index 171cd2c25..f8d9816ae 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJodaDateTime.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJodaDateTime.java @@ -6,6 +6,7 @@ import org.joda.time.DateTime; import java.sql.Timestamp; import java.sql.Types; +import java.time.Instant; /** * ScalarType for Joda DateTime. This maps to a JDBC Timestamp. @@ -41,6 +42,11 @@ public class ScalarTypeJodaDateTime extends ScalarTypeBaseDateTime { return new DateTime(ts.getTime()); } + @Override + public DateTime convertFromInstant(Instant ts) { + return new DateTime(ts.toEpochMilli()); + } + @Override public Timestamp convertToTimestamp(DateTime t) { return new Timestamp(t.getMillis()); diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJodaLocalDateTime.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJodaLocalDateTime.java index cf5d5aac3..bb5db7e37 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJodaLocalDateTime.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJodaLocalDateTime.java @@ -6,6 +6,7 @@ import org.joda.time.LocalDateTime; import java.sql.Timestamp; import java.sql.Types; +import java.time.Instant; /** * ScalarType for Joda LocalDateTime. This maps to a JDBC Timestamp. @@ -31,7 +32,6 @@ public class ScalarTypeJodaLocalDateTime extends ScalarTypeBaseDateTime { @Override protected String toJsonISO8601(Timestamp value) { - return dateTimeParser.format(value); + return formatIso(value.toInstant()); } @Override @@ -41,6 +44,11 @@ public class ScalarTypeTimestamp extends ScalarTypeBaseDateTime { return ts; } + @Override + public Timestamp convertFromInstant(Instant ts) { + return Timestamp.from(ts); + } + @Override public Timestamp convertToTimestamp(Timestamp t) { return t; diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeUtilDate.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeUtilDate.java index 0d02b81dc..ec9a3ba46 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeUtilDate.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeUtilDate.java @@ -6,8 +6,11 @@ import io.ebeaninternal.server.core.BasicTypeConverter; import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; +import java.time.Instant; import java.util.Date; +import static io.ebeaninternal.server.type.IsoJsonDateTimeParser.formatIso; + /** * ScalarType for java.util.Date. */ @@ -26,7 +29,7 @@ public class ScalarTypeUtilDate { @Override protected String toJsonISO8601(Date value) { - return dateTimeParser.format(value); + return formatIso(value.toInstant()); } @Override @@ -69,6 +72,11 @@ public class ScalarTypeUtilDate { return new java.util.Date(ts.getTime()); } + @Override + public Date convertFromInstant(Instant ts) { + return new java.util.Date(ts.toEpochMilli()); + } + @Override public Timestamp convertToTimestamp(Date date) { return new Timestamp(date.getTime()); diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTime.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTime.java index d580f563b..52b777b6b 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTime.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTime.java @@ -34,12 +34,17 @@ public class ScalarTypeZonedDateTime extends ScalarTypeBaseDateTime jsonTester = new JsonTester<>(type); @@ -139,4 +141,15 @@ public class ScalarTypeInstantTest { } + @Test + public void isoJsonParseFormat() { + + ScalarTypeInstant typeIso = new ScalarTypeInstant(JsonConfig.DateTime.ISO8601); + + Instant instant = Instant.now(); + String asJson = typeIso.toJsonISO8601(instant); + + Instant value = typeIso.fromJsonISO8601(asJson); + assertThat(instant).isEqualTo(value); + } } diff --git a/src/test/java/io/ebeaninternal/server/type/ScalarTypeLocalDateTimeTest.java b/src/test/java/io/ebeaninternal/server/type/ScalarTypeLocalDateTimeTest.java index 65850e947..88803da74 100644 --- a/src/test/java/io/ebeaninternal/server/type/ScalarTypeLocalDateTimeTest.java +++ b/src/test/java/io/ebeaninternal/server/type/ScalarTypeLocalDateTimeTest.java @@ -6,7 +6,10 @@ import org.junit.Test; import java.sql.Timestamp; import java.time.LocalDateTime; -import static org.junit.Assert.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; public class ScalarTypeLocalDateTimeTest { @@ -87,4 +90,16 @@ public class ScalarTypeLocalDateTimeTest { jsonTester = new JsonTester<>(typeIso); jsonTester.test(now); } + + @Test + public void isoJsonFormatParse() { + + ScalarTypeLocalDateTime typeIso = new ScalarTypeLocalDateTime(JsonConfig.DateTime.ISO8601); + + LocalDateTime localDateTime = LocalDateTime.now(); + String asJson = typeIso.toJsonISO8601(localDateTime); + + LocalDateTime value = typeIso.fromJsonISO8601(asJson); + assertThat(localDateTime).isEqualToIgnoringNanos(value); + } } diff --git a/src/test/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTimeTest.java b/src/test/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTimeTest.java index c950d5814..9661dd768 100644 --- a/src/test/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTimeTest.java +++ b/src/test/java/io/ebeaninternal/server/type/ScalarTypeOffsetDateTimeTest.java @@ -6,7 +6,10 @@ import org.junit.Test; import java.sql.Timestamp; import java.time.OffsetDateTime; -import static org.junit.Assert.*; +import static org.assertj.core.api.StrictAssertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; public class ScalarTypeOffsetDateTimeTest { @@ -74,4 +77,16 @@ public class ScalarTypeOffsetDateTimeTest { jsonTester = new JsonTester<>(typeIso); jsonTester.test(now); } + + @Test + public void isoJsonFormatParse() { + + ScalarTypeOffsetDateTime typeIso = new ScalarTypeOffsetDateTime(JsonConfig.DateTime.ISO8601); + + OffsetDateTime now = OffsetDateTime.now(); + String asJson = typeIso.toJsonISO8601(now); + + OffsetDateTime value = typeIso.fromJsonISO8601(asJson); + assertThat(now).isEqualToIgnoringNanos(value); + } } diff --git a/src/test/java/io/ebeaninternal/server/type/ScalarTypeTimestampTest.java b/src/test/java/io/ebeaninternal/server/type/ScalarTypeTimestampTest.java new file mode 100644 index 000000000..e5ff5d50a --- /dev/null +++ b/src/test/java/io/ebeaninternal/server/type/ScalarTypeTimestampTest.java @@ -0,0 +1,23 @@ +package io.ebeaninternal.server.type; + +import io.ebean.config.JsonConfig; +import org.junit.Test; + +import java.sql.Timestamp; + +import static org.assertj.core.api.StrictAssertions.assertThat; + +public class ScalarTypeTimestampTest { + + @Test + public void toJsonISO8601() { + + ScalarTypeTimestamp typeIso = new ScalarTypeTimestamp(JsonConfig.DateTime.ISO8601); + + Timestamp now = new Timestamp(System.currentTimeMillis()); + String asJson = typeIso.toJsonISO8601(now); + + Timestamp value = typeIso.fromJsonISO8601(asJson); + assertThat(now).isEqualTo(value); + } +} diff --git a/src/test/java/io/ebeaninternal/server/type/ScalarTypeUtilDateTest.java b/src/test/java/io/ebeaninternal/server/type/ScalarTypeUtilDateTest.java index 29be8e8fa..26e5b4511 100644 --- a/src/test/java/io/ebeaninternal/server/type/ScalarTypeUtilDateTest.java +++ b/src/test/java/io/ebeaninternal/server/type/ScalarTypeUtilDateTest.java @@ -24,4 +24,16 @@ public class ScalarTypeUtilDateTest { Date val1 = jsonIso.type.parse("2019-05-09"); assertThat(jsonIso.test(val1)).isEqualTo("{\"key\":\"2019-05-09\"}"); } + + @Test + public void toJsonISO8601() { + + ScalarTypeUtilDate.TimestampType typeIso = new ScalarTypeUtilDate.TimestampType(JsonConfig.DateTime.ISO8601); + + Date now = new Date(); + String asJson = typeIso.toJsonISO8601(now); + + Date value = typeIso.fromJsonISO8601(asJson); + assertThat(now).isEqualTo(value); + } } diff --git a/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java b/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java index be4570ac6..cf3b28aff 100644 --- a/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java +++ b/src/test/java/io/ebeaninternal/server/type/ScalarTypeZonedDateTimeTest.java @@ -6,6 +6,7 @@ import org.junit.Test; import java.sql.Timestamp; import java.time.ZonedDateTime; +import static org.assertj.core.api.StrictAssertions.assertThat; import static org.junit.Assert.*; public class ScalarTypeZonedDateTimeTest { @@ -75,4 +76,16 @@ public class ScalarTypeZonedDateTimeTest { jsonTester = new JsonTester<>(typeIso); jsonTester.test(now); } + + @Test + public void toJsonISO8601() { + + ScalarTypeZonedDateTime typeIso = new ScalarTypeZonedDateTime(JsonConfig.DateTime.ISO8601); + + ZonedDateTime now = ZonedDateTime.now(); + String asJson = typeIso.toJsonISO8601(now); + + ZonedDateTime value = typeIso.fromJsonISO8601(asJson); + assertThat(now).isEqualTo(value); + } }