mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-30 15:11:16 +02:00
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.
This commit is contained in:
parent
48123de717
commit
a239af02c3
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.46 2005/06/15 00:34:08 momjian Exp $
|
$PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.47 2005/09/09 02:31:48 tgl Exp $
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<appendix id="datetime-appendix">
|
<appendix id="datetime-appendix">
|
||||||
@ -990,9 +990,7 @@ $PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.46 2005/06/15 00:34:08 momjian
|
|||||||
<para>
|
<para>
|
||||||
<xref linkend="datetime-timezone-set-table"> shows the time zone
|
<xref linkend="datetime-timezone-set-table"> shows the time zone
|
||||||
names recognized by <productname>PostgreSQL</productname> as valid
|
names recognized by <productname>PostgreSQL</productname> as valid
|
||||||
settings for the <xref linkend="guc-timezone"> parameter, and as
|
settings for the <xref linkend="guc-timezone"> parameter. Note that
|
||||||
parameters to the <literal>AT TIME ZONE function</> (see
|
|
||||||
<xref linkend="functions-datetime-zoneconvert">). Note that
|
|
||||||
these names are conceptually as well as practically different from
|
these names are conceptually as well as practically different from
|
||||||
the names shown in <xref linkend="datetime-timezone-input-table">:
|
the names shown in <xref linkend="datetime-timezone-input-table">:
|
||||||
most of these names imply a local daylight-savings time rule, whereas
|
most of these names imply a local daylight-savings time rule, whereas
|
||||||
@ -1006,7 +1004,7 @@ $PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.46 2005/06/15 00:34:08 momjian
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<table id="datetime-timezone-set-table">
|
<table id="datetime-timezone-set-table">
|
||||||
<title>Time Zone Names for Setting <varname>timezone</> and <literal>AT TIME ZONE</></title>
|
<title>Time Zone Names for Setting <varname>timezone</></title>
|
||||||
<tgroup cols="1">
|
<tgroup cols="1">
|
||||||
<thead>
|
<thead>
|
||||||
<row>
|
<row>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.283 2005/08/25 01:29:55 momjian Exp $
|
$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.284 2005/09/09 02:31:48 tgl Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -5730,9 +5730,9 @@ SELECT date_trunc('year', TIMESTAMP '2001-02-16 20:38:40');
|
|||||||
In these expressions, the desired time zone <replaceable>zone</> can be
|
In these expressions, the desired time zone <replaceable>zone</> can be
|
||||||
specified either as a text string (e.g., <literal>'PST'</literal>)
|
specified either as a text string (e.g., <literal>'PST'</literal>)
|
||||||
or as an interval (e.g., <literal>INTERVAL '-08:00'</literal>).
|
or as an interval (e.g., <literal>INTERVAL '-08:00'</literal>).
|
||||||
In the text case, the available zone names are those shown in
|
In the text case, the available zone names are those shown in either
|
||||||
<xref linkend="datetime-timezone-set-table">. The time zone can
|
<xref linkend="datetime-timezone-set-table"> or
|
||||||
also be implied using the default time zone for that session.
|
<xref linkend="datetime-timezone-input-table">.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.119 2005/07/23 14:25:33 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.120 2005/09/09 02:31:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -2484,37 +2484,53 @@ timetz_zone(PG_FUNCTION_ARGS)
|
|||||||
TimeTzADT *t = PG_GETARG_TIMETZADT_P(1);
|
TimeTzADT *t = PG_GETARG_TIMETZADT_P(1);
|
||||||
TimeTzADT *result;
|
TimeTzADT *result;
|
||||||
int tz;
|
int tz;
|
||||||
char tzname[TZ_STRLEN_MAX];
|
char tzname[TZ_STRLEN_MAX + 1];
|
||||||
int len;
|
int len;
|
||||||
pg_tz *tzp;
|
pg_tz *tzp;
|
||||||
struct pg_tm *tm;
|
|
||||||
pg_time_t now;
|
|
||||||
|
|
||||||
/* Find the specified timezone */
|
/*
|
||||||
len = (VARSIZE(zone) - VARHDRSZ > TZ_STRLEN_MAX) ?
|
* Look up the requested timezone. First we look in the timezone
|
||||||
TZ_STRLEN_MAX : VARSIZE(zone) - VARHDRSZ;
|
* 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);
|
memcpy(tzname, VARDATA(zone), len);
|
||||||
tzname[len]=0;
|
tzname[len] = '\0';
|
||||||
tzp = pg_tzset(tzname);
|
tzp = pg_tzset(tzname);
|
||||||
if (!tzp) {
|
if (tzp)
|
||||||
|
{
|
||||||
|
/* Get the offset-from-GMT that is valid today for the selected zone */
|
||||||
|
pg_time_t now;
|
||||||
|
struct pg_tm *tm;
|
||||||
|
|
||||||
|
now = time(NULL);
|
||||||
|
tm = pg_localtime(&now, tzp);
|
||||||
|
tz = -tm->tm_gmtoff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("time zone \"%s\" not recognized", tzname)));
|
errmsg("time zone \"%s\" not recognized", tzname)));
|
||||||
PG_RETURN_NULL();
|
tz = 0; /* keep compiler quiet */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the offset-from-GMT that is valid today for the selected zone */
|
|
||||||
if ((now = time(NULL)) < 0 ||
|
|
||||||
(tm = pg_localtime(&now, tzp)) == NULL) {
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("could not determine current time")));
|
|
||||||
PG_RETURN_NULL();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
|
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
|
||||||
|
|
||||||
tz = -tm->tm_gmtoff;
|
|
||||||
#ifdef HAVE_INT64_TIMESTAMP
|
#ifdef HAVE_INT64_TIMESTAMP
|
||||||
result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
|
result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
|
||||||
while (result->time < INT64CONST(0))
|
while (result->time < INT64CONST(0))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* 0 on success
|
||||||
* -1 on out of range
|
* -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.
|
* timezone) will be used.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
@ -1113,8 +1113,8 @@ timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, char **tzn,
|
|||||||
utime = (pg_time_t) dt;
|
utime = (pg_time_t) dt;
|
||||||
if ((Timestamp) utime == dt)
|
if ((Timestamp) utime == dt)
|
||||||
{
|
{
|
||||||
struct pg_tm *tx = pg_localtime(&utime, (attimezone != NULL) ?
|
struct pg_tm *tx = pg_localtime(&utime,
|
||||||
attimezone : global_timezone);
|
attimezone ? attimezone : global_timezone);
|
||||||
|
|
||||||
tm->tm_year = tx->tm_year + 1900;
|
tm->tm_year = tx->tm_year + 1900;
|
||||||
tm->tm_mon = tx->tm_mon + 1;
|
tm->tm_mon = tx->tm_mon + 1;
|
||||||
@ -3954,42 +3954,58 @@ timestamp_zone(PG_FUNCTION_ARGS)
|
|||||||
pg_tz *tzp;
|
pg_tz *tzp;
|
||||||
char tzname[TZ_STRLEN_MAX + 1];
|
char tzname[TZ_STRLEN_MAX + 1];
|
||||||
int len;
|
int len;
|
||||||
struct pg_tm tm;
|
|
||||||
fsec_t fsec;
|
|
||||||
bool fail;
|
|
||||||
|
|
||||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
if (TIMESTAMP_NOT_FINITE(timestamp))
|
||||||
PG_RETURN_TIMESTAMPTZ(timestamp);
|
PG_RETURN_TIMESTAMPTZ(timestamp);
|
||||||
|
|
||||||
/* Find the specified timezone */
|
/*
|
||||||
len = (VARSIZE(zone) - VARHDRSZ>TZ_STRLEN_MAX) ?
|
* Look up the requested timezone. First we look in the timezone
|
||||||
TZ_STRLEN_MAX : VARSIZE(zone) - VARHDRSZ;
|
* 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);
|
memcpy(tzname, VARDATA(zone), len);
|
||||||
tzname[len] = 0;
|
tzname[len] = '\0';
|
||||||
tzp = pg_tzset(tzname);
|
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 */
|
/* Apply the timezone change */
|
||||||
fail = (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0);
|
struct pg_tm tm;
|
||||||
if (!fail)
|
fsec_t fsec;
|
||||||
{
|
|
||||||
|
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);
|
tz = DetermineTimeZoneOffset(&tm, tzp);
|
||||||
fail = (tm2timestamp(&tm, fsec, &tz, &result) != 0);
|
if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
|
||||||
}
|
|
||||||
if (fail)
|
|
||||||
{
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("could not convert to time zone \"%s\"",
|
errmsg("could not convert to time zone \"%s\"",
|
||||||
tzname)));
|
tzname)));
|
||||||
PG_RETURN_NULL();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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);
|
PG_RETURN_TIMESTAMPTZ(result);
|
||||||
@ -4109,37 +4125,59 @@ timestamptz_zone(PG_FUNCTION_ARGS)
|
|||||||
Timestamp result;
|
Timestamp result;
|
||||||
int tz;
|
int tz;
|
||||||
pg_tz *tzp;
|
pg_tz *tzp;
|
||||||
char tzname[TZ_STRLEN_MAX];
|
char tzname[TZ_STRLEN_MAX + 1];
|
||||||
int len;
|
int len;
|
||||||
struct pg_tm tm;
|
|
||||||
fsec_t fsec = 0;
|
|
||||||
|
|
||||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
if (TIMESTAMP_NOT_FINITE(timestamp))
|
||||||
PG_RETURN_NULL();
|
PG_RETURN_TIMESTAMP(timestamp);
|
||||||
|
|
||||||
/* Find the specified zone */
|
/*
|
||||||
len = (VARSIZE(zone) - VARHDRSZ > TZ_STRLEN_MAX) ?
|
* Look up the requested timezone. First we look in the timezone
|
||||||
TZ_STRLEN_MAX : VARSIZE(zone) - VARHDRSZ;
|
* 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);
|
memcpy(tzname, VARDATA(zone), len);
|
||||||
tzname[len] = 0;
|
tzname[len] = '\0';
|
||||||
tzp = pg_tzset(tzname);
|
tzp = pg_tzset(tzname);
|
||||||
|
if (tzp)
|
||||||
|
{
|
||||||
|
/* Apply the timezone change */
|
||||||
|
struct pg_tm tm;
|
||||||
|
fsec_t fsec;
|
||||||
|
|
||||||
if (!tzp)
|
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;
|
||||||
|
|
||||||
|
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,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("time zone \"%s\" not recognized", tzname)));
|
errmsg("time zone \"%s\" not recognized", tzname)));
|
||||||
|
tz = 0; /* keep compiler quiet */
|
||||||
PG_RETURN_NULL();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0 ||
|
result = dt2local(timestamp, tz);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PG_RETURN_TIMESTAMP(result);
|
PG_RETURN_TIMESTAMP(result);
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/include/pgtime.h,v 1.9 2005/07/22 03:46:34 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/pgtime.h,v 1.10 2005/09/09 02:31:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -58,7 +58,7 @@ extern const char *pg_get_timezone_name(pg_tz *tz);
|
|||||||
|
|
||||||
extern pg_tz *global_timezone;
|
extern pg_tz *global_timezone;
|
||||||
|
|
||||||
/* Maximum length of a timezone name */
|
/* Maximum length of a timezone name (not including trailing null) */
|
||||||
#define TZ_STRLEN_MAX 255
|
#define TZ_STRLEN_MAX 255
|
||||||
|
|
||||||
#endif /* _PGTIME_H */
|
#endif /* _PGTIME_H */
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.36 2005/06/26 23:32:34 tgl Exp $
|
* $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.37 2005/09/09 02:31:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -974,7 +974,7 @@ init_timezone_hashtable(void)
|
|||||||
|
|
||||||
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
|
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
|
||||||
|
|
||||||
hash_ctl.keysize = TZ_STRLEN_MAX;
|
hash_ctl.keysize = TZ_STRLEN_MAX + 1;
|
||||||
hash_ctl.entrysize = sizeof(pg_tz);
|
hash_ctl.entrysize = sizeof(pg_tz);
|
||||||
|
|
||||||
timezone_cache = hash_create("Timezones",
|
timezone_cache = hash_create("Timezones",
|
||||||
@ -997,7 +997,7 @@ pg_tzset(const char *name)
|
|||||||
pg_tz *tzp;
|
pg_tz *tzp;
|
||||||
pg_tz tz;
|
pg_tz tz;
|
||||||
|
|
||||||
if (strlen(name) >= TZ_STRLEN_MAX)
|
if (strlen(name) > TZ_STRLEN_MAX)
|
||||||
return NULL; /* not going to fit */
|
return NULL; /* not going to fit */
|
||||||
|
|
||||||
if (!timezone_cache)
|
if (!timezone_cache)
|
||||||
|
Loading…
Reference in New Issue
Block a user