diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java index d61f8020e1..3b3889cea4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java @@ -131,6 +131,7 @@ import java.util.Calendar; import java.util.EnumSet; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.TimeZone; import java.util.UUID; @@ -2492,7 +2493,13 @@ public class DefaultBinding implements Binding { parseAnyChar(string, position, " T"); LocalTime t = parseLocalTime(string, position); - return OffsetDateTime.of(d, t, parseOffset(string, position)); + ZoneOffset offset = parseOffset(string, position); + + // [#8178] PostgreSQL doesn't support negative years but expects the + // AD / BC notation + return parseBCIf(string, position) + ? OffsetDateTime.of(d.withYear(1 - d.getYear()), t, offset) + : OffsetDateTime.of(d, t, offset); } static final LocalDate parseLocalDate(String string, int[] position) { @@ -2581,6 +2588,12 @@ public class DefaultBinding implements Binding { throw new IllegalArgumentException("Expected any of \"" + expected + "\" at position " + position[0] + " in " + string); } + private static final boolean parseBCIf(String string, int[] position) { + return parseCharIf(string, position, ' ') + && parseCharIf(string, position, 'B') + && parseCharIf(string, position, 'C'); + } + private static final boolean parseCharIf(String string, int[] position, char expected) { boolean result = string.length() > position[0] && string.charAt(position[0]) == expected; @@ -2767,16 +2780,27 @@ public class DefaultBinding implements Binding { else if (val.toEpochSecond() * 1000 == PG_DATE_NEGATIVE_INFINITY) return "-infinity"; - // Remove the ISO standard T character, as some databases don't like that + // [#8178] Replace negative dates by AD/BC notation in PostgreSQL + if (family == POSTGRES && val.getYear() <= 0) + return formatEra(val); + String format = formatISO(val); + // Remove the ISO standard T character, as some databases don't like that // Replace the ISO standard Z character for UTC, as some databases don't like that + // TODO: Write a custom formatter rather than string replacing things return StringUtils.replace(format.substring(0, 10) + ' ' + format.substring(11), "Z", "+00:00"); } private static final String formatISO(OffsetDateTime val) { return val.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); } + + private static final DateTimeFormatter ERA = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.nnnnnnnnnZZZZZ G", Locale.US); + + private static final String formatEra(OffsetDateTime val) { + return val.format(ERA); + } } static final class DefaultOffsetTimeBinding extends AbstractBinding {