From 9aae81527f3174b9b6fd6366f04435310903dab2 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 22 Sep 2010 23:48:07 -0400 Subject: [PATCH] Re-allow input of Julian dates prior to 0001-01-01 AD. This was unintentionally broken in 8.4 while tightening up checking of ordinary non-Julian date inputs to forbid references to "year zero". Per bug #5672 from Benjamin Gigot. --- src/backend/utils/adt/datetime.c | 37 +++++++++++++++++--------- src/test/regress/expected/horology.out | 13 +++++++++ src/test/regress/sql/horology.sql | 3 +++ 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index b75ca09c63..34d563605b 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -42,7 +42,7 @@ static int DecodeTimezone(char *str, int *tzp); static const datetkn *datebsearch(const char *key, const datetkn *base, int nel); static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits, struct pg_tm * tm); -static int ValidateDate(int fmask, bool is2digits, bool bc, +static int ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc, struct pg_tm * tm); static void TrimTrailingZeros(char *str); static void AppendSeconds(char *cp, int sec, fsec_t fsec, @@ -795,6 +795,7 @@ DecodeDateTime(char **field, int *ftype, int nf, int dterr; int mer = HR24; bool haveTextMonth = FALSE; + bool isjulian = FALSE; bool is2digits = FALSE; bool bc = FALSE; pg_tz *namedTz = NULL; @@ -833,10 +834,12 @@ DecodeDateTime(char **field, int *ftype, int nf, errno = 0; val = strtoi(field[i], &cp, 10); - if (errno == ERANGE) + if (errno == ERANGE || val < 0) return DTERR_FIELD_OVERFLOW; j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + isjulian = TRUE; + /* Get the time zone from the end of the string */ dterr = DecodeTimezone(cp, tzp); if (dterr) @@ -1065,11 +1068,13 @@ DecodeDateTime(char **field, int *ftype, int nf, break; case DTK_JULIAN: - /*** - * previous field was a label for "julian date"? - ***/ + /* previous field was a label for "julian date" */ + if (val < 0) + return DTERR_FIELD_OVERFLOW; tmask = DTK_DATE_M; j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + isjulian = TRUE; + /* fractional Julian Day? */ if (*cp == '.') { @@ -1361,7 +1366,7 @@ DecodeDateTime(char **field, int *ftype, int nf, } /* end loop over fields */ /* do final checking/adjustment of Y/M/D fields */ - dterr = ValidateDate(fmask, is2digits, bc, tm); + dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm); if (dterr) return dterr; @@ -1564,6 +1569,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf, int i; int val; int dterr; + bool isjulian = FALSE; bool is2digits = FALSE; bool bc = FALSE; int mer = HR24; @@ -1795,11 +1801,13 @@ DecodeTimeOnly(char **field, int *ftype, int nf, break; case DTK_JULIAN: - /*** - * previous field was a label for "julian date"? - ***/ + /* previous field was a label for "julian date" */ + if (val < 0) + return DTERR_FIELD_OVERFLOW; tmask = DTK_DATE_M; j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + isjulian = TRUE; + if (*cp == '.') { double time; @@ -2045,7 +2053,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf, } /* end loop over fields */ /* do final checking/adjustment of Y/M/D fields */ - dterr = ValidateDate(fmask, is2digits, bc, tm); + dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm); if (dterr) return dterr; @@ -2247,11 +2255,16 @@ DecodeDate(char *str, int fmask, int *tmask, bool *is2digits, * Return 0 if okay, a DTERR code if not. */ static int -ValidateDate(int fmask, bool is2digits, bool bc, struct pg_tm * tm) +ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc, + struct pg_tm * tm) { if (fmask & DTK_M(YEAR)) { - if (bc) + if (isjulian) + { + /* tm_year is correct and should not be touched */ + } + else if (bc) { /* there is no year zero in AD/BC notation */ if (tm->tm_year <= 0) diff --git a/src/test/regress/expected/horology.out b/src/test/regress/expected/horology.out index 26d7541b72..b13f7d7c5b 100644 --- a/src/test/regress/expected/horology.out +++ b/src/test/regress/expected/horology.out @@ -264,6 +264,19 @@ SELECT time with time zone 'T040506.789 -08'; (1 row) SET DateStyle = 'Postgres, MDY'; +-- Check Julian dates BC +SELECT date 'J1520447' AS "Confucius' Birthday"; + Confucius' Birthday +--------------------- + 09-28-0551 BC +(1 row) + +SELECT date 'J0' AS "Julian Epoch"; + Julian Epoch +--------------- + 11-24-4714 BC +(1 row) + -- -- date, time arithmetic -- diff --git a/src/test/regress/sql/horology.sql b/src/test/regress/sql/horology.sql index 615755e3de..97ff9f20c7 100644 --- a/src/test/regress/sql/horology.sql +++ b/src/test/regress/sql/horology.sql @@ -56,6 +56,9 @@ SELECT time with time zone 'T040506.789-08'; SELECT time with time zone 'T040506.789 +08'; SELECT time with time zone 'T040506.789 -08'; SET DateStyle = 'Postgres, MDY'; +-- Check Julian dates BC +SELECT date 'J1520447' AS "Confucius' Birthday"; +SELECT date 'J0' AS "Julian Epoch"; -- -- date, time arithmetic