#1727 - Refactor internals to use Instant for json parse on date time types (#1728)

This commit is contained in:
Rob Bygrave
2019-06-10 16:16:04 +12:00
committed by GitHub
parent e26a38ecaa
commit 6af5fae8e9
20 changed files with 237 additions and 53 deletions
@@ -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);
}
}
@@ -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<T> extends ScalarTypeBase<T> {
protected final UtilDateTimeParser dateTimeParser = new UtilDateTimeParser();
protected final JsonConfig.DateTime mode;
public ScalarTypeBaseDateTime(JsonConfig.DateTime mode, Class<T> type, boolean jdbcNative, int jdbcType) {
@@ -43,6 +43,11 @@ public abstract class ScalarTypeBaseDateTime<T> extends ScalarTypeBase<T> {
*/
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<T> extends ScalarTypeBase<T> {
*/
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<T> extends ScalarTypeBase<T> {
return convertFromTimestamp(timestamp);
}
default: {
String jsonDateTime = parser.getText();
return convertFromTimestamp(dateTimeParser.parse(jsonDateTime));
return fromJsonISO8601(parser.getText());
}
}
}
@@ -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<Calendar> {
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<Calendar> {
@Override
protected String toJsonISO8601(Calendar value) {
return dateTimeParser.format(value.getTime());
return formatIso(value.toInstant());
}
@Override
@@ -25,6 +25,11 @@ public class ScalarTypeInstant extends ScalarTypeBaseDateTime<Instant> {
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<Instant> {
return ts.toInstant();
}
@Override
public Instant convertFromInstant(Instant ts) {
return ts;
}
@Override
public Timestamp convertToTimestamp(Instant t) {
return Timestamp.from(t);
@@ -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<DateTime> {
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());
@@ -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<LocalDat
return value.toDateTime().getMillis();
}
@Override
public LocalDateTime convertFromMillis(long systemTimeMillis) {
return new LocalDateTime(systemTimeMillis);
@@ -42,6 +42,11 @@ public class ScalarTypeJodaLocalDateTime extends ScalarTypeBaseDateTime<LocalDat
return new LocalDateTime(ts.getTime());
}
@Override
public LocalDateTime convertFromInstant(Instant ts) {
return new LocalDateTime(ts.toEpochMilli());
}
@Override
public Timestamp convertToTimestamp(LocalDateTime t) {
return new Timestamp(t.toDateTime().getMillis());
@@ -4,6 +4,7 @@ import io.ebean.config.JsonConfig;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
@@ -41,6 +42,11 @@ public class ScalarTypeLocalDateTime extends ScalarTypeBaseDateTime<LocalDateTim
return timestamp.toLocalDateTime();
}
@Override
public LocalDateTime convertFromInstant(Instant ts) {
return LocalDateTime.ofInstant(ts, ZoneId.systemDefault());
}
@Override
public Timestamp convertToTimestamp(LocalDateTime dateTime) {
return Timestamp.valueOf(dateTime);
@@ -8,6 +8,8 @@ import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import static io.ebeaninternal.server.type.IsoJsonDateTimeParser.formatIso;
/**
* ScalarType for java.sql.Timestamp.
*/
@@ -24,7 +26,7 @@ public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime<OffsetDateT
@Override
protected String toJsonISO8601(OffsetDateTime value) {
return value.toInstant().toString();
return formatIso(value.toInstant());
}
@Override
@@ -34,12 +36,17 @@ public class ScalarTypeOffsetDateTime extends ScalarTypeBaseDateTime<OffsetDateT
@Override
public OffsetDateTime convertFromMillis(long systemTimeMillis) {
return OffsetDateTime.ofInstant(Instant.ofEpochMilli(systemTimeMillis), ZoneId.systemDefault());
return convertFromInstant(Instant.ofEpochMilli(systemTimeMillis));
}
@Override
public OffsetDateTime convertFromTimestamp(Timestamp ts) {
return OffsetDateTime.ofInstant(ts.toInstant(), ZoneId.systemDefault());
return convertFromInstant(ts.toInstant());
}
@Override
public OffsetDateTime convertFromInstant(Instant ts) {
return OffsetDateTime.ofInstant(ts, ZoneId.systemDefault());
}
@Override
@@ -6,6 +6,9 @@ import io.ebeaninternal.server.core.BasicTypeConverter;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.Instant;
import static io.ebeaninternal.server.type.IsoJsonDateTimeParser.formatIso;
/**
* ScalarType for java.sql.Timestamp.
@@ -23,7 +26,7 @@ public class ScalarTypeTimestamp extends ScalarTypeBaseDateTime<Timestamp> {
@Override
protected String toJsonISO8601(Timestamp value) {
return dateTimeParser.format(value);
return formatIso(value.toInstant());
}
@Override
@@ -41,6 +44,11 @@ public class ScalarTypeTimestamp extends ScalarTypeBaseDateTime<Timestamp> {
return ts;
}
@Override
public Timestamp convertFromInstant(Instant ts) {
return Timestamp.from(ts);
}
@Override
public Timestamp convertToTimestamp(Timestamp t) {
return t;
@@ -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());
@@ -34,12 +34,17 @@ public class ScalarTypeZonedDateTime extends ScalarTypeBaseDateTime<ZonedDateTim
@Override
public ZonedDateTime convertFromMillis(long systemTimeMillis) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(systemTimeMillis), ZoneId.systemDefault());
return convertFromInstant(Instant.ofEpochMilli(systemTimeMillis));
}
@Override
public ZonedDateTime convertFromTimestamp(Timestamp ts) {
return ZonedDateTime.ofInstant(ts.toInstant(), ZoneId.systemDefault());
return convertFromInstant(ts.toInstant());
}
@Override
public ZonedDateTime convertFromInstant(Instant ts) {
return ZonedDateTime.ofInstant(ts, ZoneId.systemDefault());
}
@Override
@@ -1,28 +0,0 @@
package io.ebeaninternal.server.type;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.util.Date;
class UtilDateTimeParser {
private static final DateTimeFormatter ISO_MILLIS = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendInstant(3)
.toFormatter();
public Timestamp parse(String jsonDateTime) {
try {
return Timestamp.from(Instant.parse(jsonDateTime));
} catch (DateTimeParseException e) {
throw new RuntimeException("Error parsing Datetime[" + jsonDateTime + "]", e);
}
}
public String format(Date value) {
return ISO_MILLIS.format(value.toInstant());
}
}
@@ -3,13 +3,13 @@ package io.ebeaninternal.server.type;
import org.junit.Test;
import java.sql.Timestamp;
import java.time.Instant;
import static org.assertj.core.api.StrictAssertions.assertThat;
public class UtilDateTimeParserTest {
public class IsoJsonDateTimeParserTest {
private UtilDateTimeParser parser = new UtilDateTimeParser();
private IsoJsonDateTimeParser parser = new IsoJsonDateTimeParser();
@Test
public void parseFormat_when_hasMillis() {
@@ -42,8 +42,8 @@ public class UtilDateTimeParserTest {
}
private void parseAndFormat(String input, String expected) {
Timestamp timestamp = parser.parse(input);
String format = parser.format(timestamp);
Instant timestamp = parser.parseIso(input);
String format = parser.formatIso(timestamp);
assertThat(format).isEqualTo(expected);
}
}
@@ -0,0 +1,24 @@
package io.ebeaninternal.server.type;
import io.ebean.config.JsonConfig;
import org.junit.Test;
import java.sql.Types;
import java.util.Calendar;
import static org.assertj.core.api.Assertions.assertThat;
public class ScalarTypeCalendarTest {
private ScalarTypeCalendar type = new ScalarTypeCalendar(JsonConfig.DateTime.ISO8601, Types.TIMESTAMP);
@Test
public void toJsonISO8601() {
Calendar instance = Calendar.getInstance();
String asUtc = type.toJsonISO8601(instance);
Calendar calendar = type.fromJsonISO8601(asUtc);
assertThat(instance).isEqualTo(calendar);
}
}
@@ -10,7 +10,10 @@ import java.io.ObjectOutputStream;
import java.sql.Timestamp;
import java.time.Instant;
import static org.junit.Assert.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class ScalarTypeInstantTest {
@@ -123,7 +126,6 @@ public class ScalarTypeInstantTest {
@Test
public void testJsonRead() throws Exception {
Instant now = now();
JsonTester<Instant> 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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}