Re-allow input of Julian dates prior to 0001-01-01 AD.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 23 Sep 2010 03:48:07 +0000 (23:48 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 23 Sep 2010 03:48:07 +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 b75ca09c63be75ddf81015a9021bc59b6c58d2fb..34d563605bea6983b476c1602a2e3d99a4873c60 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