diff --git a/src/main/java/io/r2dbc/postgresql/codec/InstantCodec.java b/src/main/java/io/r2dbc/postgresql/codec/InstantCodec.java index 4ee4bde5..eef45439 100644 --- a/src/main/java/io/r2dbc/postgresql/codec/InstantCodec.java +++ b/src/main/java/io/r2dbc/postgresql/codec/InstantCodec.java @@ -37,7 +37,7 @@ final class InstantCodec extends AbstractTemporalCodec { private final Supplier zoneIdSupplier; InstantCodec(ByteBufAllocator byteBufAllocator, Supplier zoneIdSupplier) { - super(Instant.class, byteBufAllocator, TIMESTAMPTZ, TIMESTAMPTZ_ARRAY, Instant::toString); + super(Instant.class, byteBufAllocator, TIMESTAMPTZ, TIMESTAMPTZ_ARRAY, PostgresqlDateTimeFormatter::format); this.zoneIdSupplier = zoneIdSupplier; } diff --git a/src/main/java/io/r2dbc/postgresql/codec/PostgresqlDateTimeFormatter.java b/src/main/java/io/r2dbc/postgresql/codec/PostgresqlDateTimeFormatter.java index 08ec4741..bf3f0520 100644 --- a/src/main/java/io/r2dbc/postgresql/codec/PostgresqlDateTimeFormatter.java +++ b/src/main/java/io/r2dbc/postgresql/codec/PostgresqlDateTimeFormatter.java @@ -16,25 +16,30 @@ package io.r2dbc.postgresql.codec; +import java.time.Instant; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.format.SignStyle; +import java.time.temporal.Temporal; import java.time.temporal.TemporalQuery; +import static java.time.ZoneOffset.UTC; +import static java.time.format.TextStyle.SHORT; import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.ERA; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; -import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; class PostgresqlDateTimeFormatter { private static final DateTimeFormatter FULL_OFFSET = new DateTimeFormatterBuilder() - .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendValue(YEAR_OF_ERA, 4, 10, SignStyle.NEVER) .appendLiteral('-') .appendValue(MONTH_OF_YEAR, 2) .appendLiteral('-') @@ -52,11 +57,15 @@ class PostgresqlDateTimeFormatter { .optionalStart() .appendOffset("+HH:MM:ss", "+00:00:00") .optionalEnd() + .optionalStart() + .appendLiteral(' ') + .appendText(ERA, SHORT) + .optionalEnd() .toFormatter(); private static final DateTimeFormatter SHORT_OFFSET = new DateTimeFormatterBuilder() - .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendValue(YEAR_OF_ERA, 4, 10, SignStyle.NEVER) .appendLiteral('-') .appendValue(MONTH_OF_YEAR, 2) .appendLiteral('-') @@ -74,6 +83,10 @@ class PostgresqlDateTimeFormatter { .optionalStart() .appendOffset("+HH:mm", "+00:00") .optionalEnd() + .optionalStart() + .appendLiteral(' ') + .appendText(ERA, SHORT) + .optionalEnd() .toFormatter(); /** @@ -104,4 +117,11 @@ static T parse(CharSequence text, TemporalQuery query) { } } + static String format(Temporal temporal) { + if (temporal instanceof Instant) { + temporal = ((Instant) temporal).atOffset(UTC); + } + return SHORT_OFFSET.format(temporal); + } + } diff --git a/src/test/java/io/r2dbc/postgresql/AbstractCodecIntegrationTests.java b/src/test/java/io/r2dbc/postgresql/AbstractCodecIntegrationTests.java index daf3d648..cfd94fe4 100644 --- a/src/test/java/io/r2dbc/postgresql/AbstractCodecIntegrationTests.java +++ b/src/test/java/io/r2dbc/postgresql/AbstractCodecIntegrationTests.java @@ -325,6 +325,7 @@ void inetAddressArray() throws UnknownHostException { @Test void instant() { testCodec(Instant.class, Instant.now().truncatedTo(ChronoUnit.MICROS), "TIMESTAMPTZ"); + testCodec(Instant.class, Instant.parse("0000-12-31T01:01:00Z"), "TIMESTAMPTZ"); } @Test diff --git a/src/test/java/io/r2dbc/postgresql/codec/InstantCodecUnitTests.java b/src/test/java/io/r2dbc/postgresql/codec/InstantCodecUnitTests.java index 8a30c039..6ebd61e6 100644 --- a/src/test/java/io/r2dbc/postgresql/codec/InstantCodecUnitTests.java +++ b/src/test/java/io/r2dbc/postgresql/codec/InstantCodecUnitTests.java @@ -126,7 +126,7 @@ void doEncode() { assertThat(this.codec.doEncode(instant)) .hasFormat(FORMAT_TEXT) .hasType(TIMESTAMPTZ.getObjectId()) - .hasValue(encode(TEST, instant.toString())); + .hasValue(encode(TEST, PostgresqlDateTimeFormatter.format(instant))); } @Test