Skip to content

Commit fb70c90

Browse files
committed
Fix DateTimeTypesExtendedCodec tests
Use the timestamp min/max values from PostgreSQL server Then, it's safe to add microseconds to the computed timestamps because we're well under the limit of LocalDateTime.MAX Signed-off-by: Thomas Segismont <tsegismont@gmail.com>
1 parent c05568b commit fb70c90

1 file changed

Lines changed: 40 additions & 34 deletions

File tree

vertx-pg-client/src/main/java/io/vertx/pgclient/impl/codec/DataTypeCodec.java

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,17 @@
4242
import java.nio.charset.StandardCharsets;
4343
import java.text.DecimalFormat;
4444
import java.time.*;
45-
import java.time.format.DateTimeFormatter;
4645
import java.time.format.DateTimeFormatterBuilder;
4746
import java.time.temporal.ChronoField;
48-
import java.time.temporal.ChronoUnit;
4947
import java.util.ArrayList;
5048
import java.util.List;
51-
import java.util.Locale;
5249
import java.util.UUID;
5350
import java.util.function.IntFunction;
5451

5552
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
5653
import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME;
54+
import static java.time.temporal.ChronoUnit.DAYS;
55+
import static java.time.temporal.ChronoUnit.MICROS;
5756

5857
/**
5958
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
@@ -91,8 +90,7 @@ public class DataTypeCodec {
9190
private static final Float[] empty_float_array = new Float[0];
9291
private static final Double[] empty_double_array = new Double[0];
9392
private static final LocalDate LOCAL_DATE_EPOCH = LocalDate.of(2000, 1, 1);
94-
private static final LocalDateTime LOCAL_DATE_TIME_EPOCH = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
95-
private static final OffsetDateTime OFFSET_DATE_TIME_EPOCH = LocalDateTime.of(2000, 1, 1, 0, 0, 0).atOffset(ZoneOffset.UTC);
93+
private static final LocalDateTime LOCAL_DATE_TIME_EPOCH = LOCAL_DATE_EPOCH.atStartOfDay();
9694
private static final Inet[] empty_inet_array = new Inet[0];
9795
private static final Money[] empty_money_array = new Money[0];
9896

@@ -1017,7 +1015,7 @@ private static void binaryEncodeDATE(LocalDate value, ByteBuf buff) {
10171015
} else if (value == LocalDate.MIN) {
10181016
v = Integer.MIN_VALUE;
10191017
} else {
1020-
v = (int) -value.until(LOCAL_DATE_EPOCH, ChronoUnit.DAYS);
1018+
v = (int) -value.until(LOCAL_DATE_EPOCH, DAYS);
10211019
}
10221020
buff.writeInt(v);
10231021
}
@@ -1030,7 +1028,7 @@ private static LocalDate binaryDecodeDATE(int index, int len, ByteBuf buff) {
10301028
case Integer.MIN_VALUE:
10311029
return LocalDate.MIN;
10321030
default:
1033-
return LOCAL_DATE_EPOCH.plus(val, ChronoUnit.DAYS);
1031+
return LOCAL_DATE_EPOCH.plus(val, DAYS);
10341032
}
10351033
}
10361034

@@ -1079,37 +1077,45 @@ private static OffsetTime textDecodeTIMETZ(int index, int len, ByteBuf buff) {
10791077
return OffsetTime.parse(cs, TIMETZ_FORMAT);
10801078
}
10811079

1082-
// 294277-01-09 04:00:54.775807
1083-
public static final LocalDateTime LDT_PLUS_INFINITY = LOCAL_DATE_TIME_EPOCH.plus(Long.MAX_VALUE, ChronoUnit.MICROS);
1084-
// 4714-11-24 00:00:00 BC
1085-
public static final LocalDateTime LDT_MINUS_INFINITY = LocalDateTime.parse("4714-11-24 00:00:00 BC",
1086-
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss G", Locale.US));
1080+
/*
1081+
* See limits for dates and timestamps
1082+
* https://github.com/postgres/postgres/blob/75e82b2f5a6f5de6b42dbd9ea73be5ff36a931b1/src/include/datatype/timestamp.h
1083+
*
1084+
* In Pg Client, we define inclusive min and max.
1085+
*/
1086+
private static final long MIN_TIMESTAMP = -211813488000000000L;
1087+
private static final LocalDateTime MIN_LDT = LOCAL_DATE_TIME_EPOCH.plus(MIN_TIMESTAMP, MICROS);
1088+
private static final long MAX_TIMESTAMP = 9223371331200000000L - 1;
1089+
private static final LocalDateTime MAX_LDT = LOCAL_DATE_TIME_EPOCH.plus(MAX_TIMESTAMP, MICROS);
10871090

10881091
private static void binaryEncodeTIMESTAMP(LocalDateTime value, ByteBuf buff) {
1089-
// Make sure to handle microsecond resolution as PostgreSQL does when it converts textual representation of timestamps.
1090-
// Over 499 nanos after the microsecond, round up to the next microsecond.
1091-
// Otherwise, do nothing and the nanos after the microsecond will be truncated below.
1092-
int nanosAfterMicro = value.getNano() % 1000;
1093-
if (nanosAfterMicro > 499) {
1094-
value = value.plusNanos(1000 - nanosAfterMicro);
1095-
}
1096-
if (value.compareTo(LDT_PLUS_INFINITY) >= 0) {
1097-
value = LDT_PLUS_INFINITY;
1098-
} else if (value.compareTo(LDT_MINUS_INFINITY) <= 0) {
1099-
value = LDT_MINUS_INFINITY;
1092+
long timestamp;
1093+
if (MIN_LDT.isAfter(value)) {
1094+
timestamp = MIN_TIMESTAMP;
1095+
} else if (value.isAfter(MAX_LDT)) {
1096+
timestamp = MAX_TIMESTAMP;
1097+
} else {
1098+
// Make sure to handle microsecond resolution as PostgreSQL does when it converts textual representation of timestamps.
1099+
// Over 499 nanos after the microsecond, round up to the next microsecond.
1100+
// Otherwise, do nothing and the nanos after the microsecond will be truncated below.
1101+
int nanosAfterMicro = value.getNano() % 1000;
1102+
if (nanosAfterMicro > 499) {
1103+
value = value.plusNanos(1000 - nanosAfterMicro);
1104+
}
1105+
timestamp = Math.min(MAX_TIMESTAMP, -value.until(LOCAL_DATE_TIME_EPOCH, MICROS));
11001106
}
1101-
buff.writeLong(-value.until(LOCAL_DATE_TIME_EPOCH, ChronoUnit.MICROS));
1107+
buff.writeLong(timestamp);
11021108
}
11031109

11041110
private static LocalDateTime binaryDecodeTIMESTAMP(int index, int len, ByteBuf buff) {
1105-
LocalDateTime val = LOCAL_DATE_TIME_EPOCH.plus(buff.getLong(index), ChronoUnit.MICROS);
1106-
if (LDT_PLUS_INFINITY.equals(val)) {
1107-
return LocalDateTime.MAX;
1108-
} else if (LDT_MINUS_INFINITY.equals(val)) {
1111+
long timestamp = buff.getLong(index);
1112+
if (timestamp <= MIN_TIMESTAMP) {
11091113
return LocalDateTime.MIN;
1110-
} else {
1111-
return val;
11121114
}
1115+
if (timestamp >= MAX_TIMESTAMP) {
1116+
return LocalDateTime.MAX;
1117+
}
1118+
return LOCAL_DATE_TIME_EPOCH.plus(timestamp, MICROS);
11131119
}
11141120

11151121
private static LocalDateTime textDecodeTIMESTAMP(int index, int len, ByteBuf buff) {
@@ -1139,12 +1145,12 @@ private static OffsetDateTime binaryDecodeTIMESTAMPTZ(int index, int len, ByteBu
11391145
private static void binaryEncodeTIMESTAMPTZ(OffsetDateTime value, ByteBuf buff) {
11401146
LocalDateTime ldt;
11411147
if (value.getOffset() != ZoneOffset.UTC) {
1142-
OffsetDateTime max = OffsetDateTime.of(LDT_PLUS_INFINITY, ZoneOffset.UTC);
1143-
if (value.compareTo(max) >= 0) {
1148+
OffsetDateTime max = OffsetDateTime.of(MAX_LDT, ZoneOffset.UTC);
1149+
if (!value.isBefore(max)) {
11441150
ldt = LocalDateTime.MAX;
11451151
} else {
1146-
OffsetDateTime min = OffsetDateTime.of(LDT_MINUS_INFINITY, ZoneOffset.UTC);
1147-
if (value.compareTo(min) <= 0) {
1152+
OffsetDateTime min = OffsetDateTime.of(MIN_LDT, ZoneOffset.UTC);
1153+
if (!value.isAfter(min)) {
11481154
ldt = LocalDateTime.MIN;
11491155
} else {
11501156
ldt = value.toInstant().atOffset(ZoneOffset.UTC).toLocalDateTime();

0 commit comments

Comments
 (0)