Allow timezone names in SQL strings,
authorBruce Momjian
Wed, 7 Jun 2006 22:32:31 +0000 (22:32 +0000)
committerBruce Momjian
Wed, 7 Jun 2006 22:32:31 +0000 (22:32 +0000)
'2006-05-24 21:11 Americas/New_York'::timestamptz

Joachim Wieland

src/backend/utils/adt/datetime.c

index 8f0146ad0e32647c44d1a10eb3d4db3ab05343cc..c02ae2a172c40beee1b4d57ef85be6a0f69c48e9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.166 2006/03/05 15:58:41 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.167 2006/06/07 22:32:31 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,7 @@
 
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "utils/builtins.h"
 #include "utils/datetime.h"
 #include "utils/guc.h"
 
@@ -36,6 +37,7 @@ static int DecodeTime(char *str, int fmask, int *tmask,
           struct pg_tm * tm, fsec_t *fsec);
 static int DecodeTimezone(char *str, int *tzp);
 static int DecodePosixTimezone(char *str, int *tzp);
+static int DecodeZicTimezone(char *str, int *tzp, struct pg_tm * tm);
 static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
 static int DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm);
 static void TrimTrailingZeros(char *str);
@@ -888,8 +890,24 @@ ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
            {
                char        delim = *cp;
 
-               ftype[nf] = DTK_DATE;
-               APPEND_CHAR(bufp, bufend, *cp++);
+               if (*cp == '/')
+               {
+                   ftype[nf] = DTK_TZ;
+                   /* set the first character of the region to upper case
+                    * again*/
+                   field[nf][0] = pg_toupper((unsigned char) field[nf][0]);
+                   /* we have seen "Region/" of a POSIX timezone, continue to
+                    * read the City part */
+                   do {
+                       APPEND_CHAR(bufp, bufend, *cp++);
+                       /* there is for example America/New_York */
+                   } while (isalpha((unsigned char) *cp) || *cp == '_');
+               }
+               else
+               {
+                   ftype[nf] = DTK_DATE;
+                   APPEND_CHAR(bufp, bufend, *cp++);
+               }
                while (isdigit((unsigned char) *cp) || *cp == delim)
                    APPEND_CHAR(bufp, bufend, *cp++);
            }
@@ -980,6 +998,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
    bool        haveTextMonth = FALSE;
    int         is2digits = FALSE;
    int         bc = FALSE;
+   int         zicTzFnum = -1;
 
    /*
     * We'll insist on at least all of the date fields, but initialize the
@@ -1127,7 +1146,15 @@ DecodeDateTime(char **field, int *ftype, int nf,
                    if (tzp == NULL)
                        return DTERR_BAD_FORMAT;
 
-                   dterr = DecodeTimezone(field[i], &tz);
+                   if (strchr(field[i], '/') != NULL)
+                   {
+                       /* remember to apply the timezone at the end */
+                       zicTzFnum = i;
+                       tmask = DTK_M(TZ);
+                       break;
+                   }
+                   else
+                       dterr = DecodeTimezone(field[i], &tz);
                    if (dterr)
                        return dterr;
 
@@ -1605,6 +1632,19 @@ DecodeDateTime(char **field, int *ftype, int nf,
        if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
            return DTERR_FIELD_OVERFLOW;
 
+       if (zicTzFnum != -1)
+       {
+           Datum tsTz;
+           Timestamp timestamp;
+           tm2timestamp(tm, *fsec, NULL, ×tamp);
+           tsTz = DirectFunctionCall2(timestamp_zone,
+                           DirectFunctionCall1(textin,
+                                           CStringGetDatum(field[zicTzFnum])),
+                           TimestampGetDatum(timestamp));
+           timestamp2tm(DatumGetTimestampTz(tsTz), tzp, tm, fsec, NULL, NULL);
+           fmask &= ~DTK_M(TZ);
+       }
+
        /* timezone not specified? then find local timezone if possible */
        if (tzp != NULL && !(fmask & DTK_M(TZ)))
        {
@@ -1874,7 +1914,15 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
                    if (tzp == NULL)
                        return DTERR_BAD_FORMAT;
 
-                   dterr = DecodeTimezone(field[i], &tz);
+                   if (strchr(field[i], '/') != NULL)
+                   {
+                       /* a date has to be specified */
+                       if ((fmask & DTK_DATE_M) != DTK_DATE_M)
+                           return DTERR_BAD_FORMAT;
+                       dterr = DecodeZicTimezone(field[i], &tz, tm);
+                   }
+                   else
+                       dterr = DecodeTimezone(field[i], &tz);
                    if (dterr)
                        return dterr;
 
@@ -2924,6 +2972,19 @@ DecodePosixTimezone(char *str, int *tzp)
    return 0;
 }
 
+static int
+DecodeZicTimezone(char *str, int *tzp, struct pg_tm * tm)
+{
+   struct pg_tz *tz;
+
+   tz = pg_tzset(str);
+   if (!tz)
+       return DTERR_BAD_FORMAT;
+
+   *tzp = DetermineTimeZoneOffset(tm, tz);
+
+   return 0;
+}
 
 /* DecodeSpecial()
  * Decode text string using lookup table.