Hello! I'd like to propose a simple patch to allow for negative ISO 8601 intervals with leading minus, e.g. -PT1H besides PT-1H. It seems that standard isn't quite clear on negative duration. However, lots of software use leading minus and expect/generate intervals in such forms making those incompatible with current PostgreSQL decoding code.
All patch is doing is making a note of a leading minus and negates pg_tm components along with fractional seconds. No other behavior change is introduced. -- Mikhail
From c1d96252ebd727b205e1ee66f34f8b564eb05c00 Mon Sep 17 00:00:00 2001 From: Mikhail Titov <mlt at gmx dot us> Date: Wed, 3 Jun 2020 02:29:38 -0500 Subject: [PATCH] Support leading minus in front of ISO 8601 interval string It is a common extension for a standard to have negative intervals prefixed with a leading minus instead of individual parts. For example -PT1H as an alternative to PT-1H. The standard itself is not specific on negative intervals. This patch makes PostgreSQL a bit more compatible with 3-rd party software that use ISO 8601 style intervals that might be negative, e.g. * https://github.com/rails/rails/issues/39503 * https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-java.lang.CharSequence- --- src/backend/utils/adt/datetime.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 1914138a24..f79df368a2 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -3492,10 +3492,17 @@ DecodeISO8601Interval(char *str, { bool datepart = true; bool havefield = false; + bool negative = false; *dtype = DTK_DELTA; ClearPgTm(tm, fsec); + if (str[0] == '-') + { + negative = true; + str++; + } + if (strlen(str) < 2 || str[0] != 'P') return DTERR_BAD_FORMAT; @@ -3681,6 +3688,19 @@ DecodeISO8601Interval(char *str, havefield = true; } + if (negative) + { + tm->tm_sec = -tm->tm_sec; + tm->tm_min = -tm->tm_min; + tm->tm_hour = -tm->tm_hour; + tm->tm_mday = -tm->tm_mday; + tm->tm_mon = -tm->tm_mon; + tm->tm_year = -tm->tm_year; + tm->tm_wday = -tm->tm_wday; + tm->tm_yday = -tm->tm_yday; + *fsec = -*fsec; + } + return 0; } -- 2.26.0.windows.1