aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/timestamp.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2005-09-09 02:31:50 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2005-09-09 02:31:50 +0000
commita239af02c3585f4355737230bc54902e8217f76e (patch)
tree0b14018187b6cd72bc07d0212b8089566dffa9dd /src/backend/utils/adt/timestamp.c
parent48123de71792c367313542883f0e9df7d850c994 (diff)
downloadpostgresql-a239af02c3585f4355737230bc54902e8217f76e.tar.gz
postgresql-a239af02c3585f4355737230bc54902e8217f76e.zip
Fix the various forms of AT TIME ZONE to accept either timezones found
in the zic database or zone names found in the date token table. This preserves the old ability to do AT TIME ZONE 'PST' along with the new ability to do AT TIME ZONE 'PST8PDT'. Per gripe from Bricklen Anderson. Also, fix some inconsistencies in usage of TZ_STRLEN_MAX --- the old code had the potential for one-byte buffer overruns, though given alignment considerations it's unlikely there was any real risk.
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
-rw-r--r--src/backend/utils/adt/timestamp.c142
1 files changed, 90 insertions, 52 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 2b15e64e061..b2d6f774c2f 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.151 2005/08/25 05:01:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.152 2005/09/09 02:31:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1014,7 +1014,7 @@ dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
* 0 on success
* -1 on out of range
*
- * If attimezone is NULL, the global timezone (including possblly brute forced
+ * If attimezone is NULL, the global timezone (including possibly brute forced
* timezone) will be used.
*/
int
@@ -1113,8 +1113,8 @@ timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, char **tzn,
utime = (pg_time_t) dt;
if ((Timestamp) utime == dt)
{
- struct pg_tm *tx = pg_localtime(&utime, (attimezone != NULL) ?
- attimezone : global_timezone);
+ struct pg_tm *tx = pg_localtime(&utime,
+ attimezone ? attimezone : global_timezone);
tm->tm_year = tx->tm_year + 1900;
tm->tm_mon = tx->tm_mon + 1;
@@ -3948,48 +3948,64 @@ Datum
timestamp_zone(PG_FUNCTION_ARGS)
{
text *zone = PG_GETARG_TEXT_P(0);
- Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
+ Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
TimestampTz result;
int tz;
pg_tz *tzp;
- char tzname[TZ_STRLEN_MAX+1];
+ char tzname[TZ_STRLEN_MAX + 1];
int len;
- struct pg_tm tm;
- fsec_t fsec;
- bool fail;
if (TIMESTAMP_NOT_FINITE(timestamp))
PG_RETURN_TIMESTAMPTZ(timestamp);
- /* Find the specified timezone */
- len = (VARSIZE(zone) - VARHDRSZ>TZ_STRLEN_MAX) ?
- TZ_STRLEN_MAX : VARSIZE(zone) - VARHDRSZ;
+ /*
+ * Look up the requested timezone. First we look in the timezone
+ * database (to handle cases like "America/New_York"), and if that
+ * fails, we look in the date token table (to handle cases like "EST").
+ */
+ len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
memcpy(tzname, VARDATA(zone), len);
- tzname[len] = 0;
+ tzname[len] = '\0';
tzp = pg_tzset(tzname);
- if (!tzp)
+ if (tzp)
{
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("time zone \"%s\" not recognised",
- tzname)));
- PG_RETURN_NULL();
- }
+ /* Apply the timezone change */
+ struct pg_tm tm;
+ fsec_t fsec;
- /* Apply the timezone change */
- fail = (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0);
- if (!fail)
- {
+ if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
tz = DetermineTimeZoneOffset(&tm, tzp);
- fail = (tm2timestamp(&tm, fsec, &tz, &result) != 0);
+ if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not convert to time zone \"%s\"",
+ tzname)));
}
- if (fail)
+ else
{
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("could not convert to time zone \"%s\"",
- tzname)));
- PG_RETURN_NULL();
+ char *lowzone;
+ int type,
+ val;
+
+ lowzone = downcase_truncate_identifier(VARDATA(zone),
+ VARSIZE(zone) - VARHDRSZ,
+ false);
+ type = DecodeSpecial(0, lowzone, &val);
+
+ if (type == TZ || type == DTZ)
+ tz = -(val * 60);
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("time zone \"%s\" not recognized", tzname)));
+ tz = 0; /* keep compiler quiet */
+ }
+
+ result = dt2local(timestamp, tz);
}
PG_RETURN_TIMESTAMPTZ(result);
@@ -4109,37 +4125,59 @@ timestamptz_zone(PG_FUNCTION_ARGS)
Timestamp result;
int tz;
pg_tz *tzp;
- char tzname[TZ_STRLEN_MAX];
+ char tzname[TZ_STRLEN_MAX + 1];
int len;
- struct pg_tm tm;
- fsec_t fsec = 0;
if (TIMESTAMP_NOT_FINITE(timestamp))
- PG_RETURN_NULL();
+ PG_RETURN_TIMESTAMP(timestamp);
- /* Find the specified zone */
- len = (VARSIZE(zone) - VARHDRSZ > TZ_STRLEN_MAX) ?
- TZ_STRLEN_MAX : VARSIZE(zone) - VARHDRSZ;
+ /*
+ * Look up the requested timezone. First we look in the timezone
+ * database (to handle cases like "America/New_York"), and if that
+ * fails, we look in the date token table (to handle cases like "EST").
+ */
+ len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
memcpy(tzname, VARDATA(zone), len);
- tzname[len] = 0;
+ tzname[len] = '\0';
tzp = pg_tzset(tzname);
-
- if (!tzp)
+ if (tzp)
{
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("time zone \"%s\" not recognized", tzname)));
+ /* Apply the timezone change */
+ struct pg_tm tm;
+ fsec_t fsec;
- PG_RETURN_NULL();
+ if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
+ if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not convert to time zone \"%s\"",
+ tzname)));
}
+ else
+ {
+ char *lowzone;
+ int type,
+ val;
- if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0 ||
- tm2timestamp(&tm, fsec, NULL, &result))
- {
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("could not to convert to time zone \"%s\"", tzname)));
- PG_RETURN_NULL();
+ lowzone = downcase_truncate_identifier(VARDATA(zone),
+ VARSIZE(zone) - VARHDRSZ,
+ false);
+ type = DecodeSpecial(0, lowzone, &val);
+
+ if (type == TZ || type == DTZ)
+ tz = val * 60;
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("time zone \"%s\" not recognized", tzname)));
+ tz = 0; /* keep compiler quiet */
+ }
+
+ result = dt2local(timestamp, tz);
}
PG_RETURN_TIMESTAMP(result);