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

Reply via email to