Re-allow input of Julian dates prior to 0001-01-01 AD.
authorTom Lane <[email protected]>
Thu, 23 Sep 2010 03:48:14 +0000 (23:48 -0400)
committerTom Lane <[email protected]>
Thu, 23 Sep 2010 03:48:14 +0000 (23:48 -0400)
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
src/test/regress/expected/horology.out
src/test/regress/sql/horology.sql

index 8e342a9837f8b9eb6ff70b530ba0f8764b6bc842..705fcf195c2e2300d92680f90cee278fdcf11355 100644 (file)
@@ -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)
index 26d7541b720117d609dc34f3aaacf6cf90754b92..b13f7d7c5b531b41aaab96439748265b1e0a3609 100644 (file)
@@ -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
 --
index 615755e3de06d7e38c863aa7fbed89ed365e1cae..97ff9f20c798f421df911c44144557de5bb79c08 100644 (file)
@@ -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