From 0851a6fbc77e7f1762a2a94d370492f03450e922 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Wed, 15 Jun 2005 00:34:11 +0000 Subject: [PATCH] This patch makes it possible to use the full set of timezones when doing "AT TIME ZONE", and not just the shorlist previously available. For example: SELECT CURRENT_TIMESTAMP AT TIME ZONE 'Europe/London'; works fine now. It will also obey whatever DST rules were in effect at just that date, which the previous implementation did not. It also supports the AT TIME ZONE on the timetz datatype. The whole handling of DST is a bit bogus there, so I chose to make it use whatever DST rules are in effect at the time of executig the query. not sure if anybody is actuallyi *using* timetz though, it seems pretty unpredictable just because of this... Magnus Hagander --- doc/src/sgml/datetime.sgml | 8 +- doc/src/sgml/func.sgml | 7 +- src/backend/utils/adt/date.c | 92 ++++++++++--------- src/backend/utils/adt/formatting.c | 6 +- src/backend/utils/adt/nabstime.c | 6 +- src/backend/utils/adt/timestamp.c | 141 +++++++++++++++-------------- src/include/pgtime.h | 6 +- src/include/utils/timestamp.h | 4 +- src/timezone/pgtz.h | 4 +- 9 files changed, 145 insertions(+), 129 deletions(-) diff --git a/doc/src/sgml/datetime.sgml b/doc/src/sgml/datetime.sgml index 0e42e34fd6..9610dc36c5 100644 --- a/doc/src/sgml/datetime.sgml +++ b/doc/src/sgml/datetime.sgml @@ -1,5 +1,5 @@ @@ -990,7 +990,9 @@ $PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.45 2005/01/09 18:58:10 tgl Exp shows the time zone names recognized by PostgreSQL as valid - settings for the parameter. Note that + settings for the parameter, and as + parameters to the AT TIME ZONE function (see + ). Note that these names are conceptually as well as practically different from the names shown in : most of these names imply a local daylight-savings time rule, whereas @@ -1004,7 +1006,7 @@ $PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.45 2005/01/09 18:58:10 tgl Exp - Time Zone Names for Setting <varname>timezone</> + Time Zone Names for Setting <varname>timezone</> and <literal>AT TIME ZONE</> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 4fe5e1d6fc..38e2c4a700 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,5 +1,5 @@ @@ -5679,10 +5679,7 @@ SELECT date_trunc('year', TIMESTAMP '2001-02-16 20:38:40'); specified either as a text string (e.g., 'PST') or as an interval (e.g., INTERVAL '-08:00'). In the text case, the available zone names are those shown in - . (It would be useful - to support the more general names shown in - , but this is not yet - implemented.) + . diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 5371c64250..abc6155594 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.109 2005/05/26 02:04:13 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.110 2005/06/15 00:34:08 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +18,7 @@ #include #include #include +#include #include "access/hash.h" #include "libpq/pqformat.h" @@ -724,7 +725,7 @@ timestamp_date(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) PG_RETURN_NULL(); - if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0) + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -767,7 +768,7 @@ timestamptz_date(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) PG_RETURN_NULL(); - if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0) + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -1327,7 +1328,7 @@ timestamp_time(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) PG_RETURN_NULL(); - if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0) + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -1364,7 +1365,7 @@ timestamptz_time(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) PG_RETURN_NULL(); - if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0) + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -2247,7 +2248,7 @@ timestamptz_timetz(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) PG_RETURN_NULL(); - if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0) + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -2463,54 +2464,61 @@ timetz_part(PG_FUNCTION_ARGS) /* timetz_zone() * Encode time with time zone type with specified time zone. + * Applies DST rules as of the current date. */ Datum timetz_zone(PG_FUNCTION_ARGS) { text *zone = PG_GETARG_TEXT_P(0); - TimeTzADT *time = PG_GETARG_TIMETZADT_P(1); + TimeTzADT *t = PG_GETARG_TIMETZADT_P(1); TimeTzADT *result; int tz; - int type, - val; - char *lowzone; + char tzname[TZ_STRLEN_MAX]; + int len; + pg_tz *tzp; + struct pg_tm *tm; + pg_time_t now; - lowzone = downcase_truncate_identifier(VARDATA(zone), - VARSIZE(zone) - VARHDRSZ, - false); - - type = DecodeSpecial(0, lowzone, &val); - - result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); - - if (type == TZ || type == DTZ) - { - tz = val * 60; -#ifdef HAVE_INT64_TIMESTAMP - result->time = time->time + (time->zone - tz) * USECS_PER_SEC; - while (result->time < INT64CONST(0)) - result->time += USECS_PER_DAY; - while (result->time >= USECS_PER_DAY) - result->time -= USECS_PER_DAY; -#else - result->time = time->time + (time->zone - tz); - while (result->time < 0) - result->time += SECS_PER_DAY; - while (result->time >= SECS_PER_DAY) - result->time -= SECS_PER_DAY; -#endif - - result->zone = tz; - } - else - { + /* Find the specified timezone */ + len = (VARSIZE(zone)-VARHDRSZ>TZ_STRLEN_MAX)?TZ_STRLEN_MAX:(VARSIZE(zone)-VARHDRSZ); + memcpy(tzname,VARDATA(zone),len); + tzname[len]=0; + tzp = pg_tzset(tzname); + if (!tzp) { ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone \"%s\" not recognized", lowzone))); - + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("time zone \"%s\" not recognized", tzname))); PG_RETURN_NULL(); } + /* 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)); + + tz = -tm->tm_gmtoff; +#ifdef HAVE_INT64_TIMESTAMP + result->time = t->time + (t->zone - tz) * USECS_PER_SEC; + while (result->time < INT64CONST(0)) + result->time += USECS_PER_DAY; + while (result->time >= USECS_PER_DAY) + result->time -= USECS_PER_DAY; +#else + result->time = t->time + (t->zone - tz); + while (result->time < 0) + result->time += SECS_PER_DAY; + while (result->time >= SECS_PER_DAY) + result->time -= SECS_PER_DAY; +#endif + + result->zone = tz; + PG_RETURN_TIMETZADT_P(result); } /* timetz_zone() */ diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 68a73bbb6f..e4de64444e 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- * formatting.c * - * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.87 2005/05/25 21:40:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.88 2005/06/15 00:34:08 momjian Exp $ * * * Portions Copyright (c) 1999-2005, PostgreSQL Global Development Group @@ -2910,7 +2910,7 @@ timestamp_to_char(PG_FUNCTION_ARGS) ZERO_tmtc(&tmtc); - if (timestamp2tm(dt, NULL, tmtcTm(&tmtc), &tmtcFsec(&tmtc), NULL) != 0) + if (timestamp2tm(dt, NULL, tmtcTm(&tmtc), &tmtcFsec(&tmtc), NULL, NULL) != 0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -2935,7 +2935,7 @@ timestamptz_to_char(PG_FUNCTION_ARGS) ZERO_tmtc(&tmtc); - if (timestamp2tm(dt, &tz, tmtcTm(&tmtc), &tmtcFsec(&tmtc), &tmtcTzn(&tmtc)) != 0) + if (timestamp2tm(dt, &tz, tmtcTm(&tmtc), &tmtcFsec(&tmtc), &tmtcTzn(&tmtc), NULL) != 0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c index bebd3f9d32..d712d9f8b4 100644 --- a/src/backend/utils/adt/nabstime.c +++ b/src/backend/utils/adt/nabstime.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.132 2005/05/26 02:04:13 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.133 2005/06/15 00:34:08 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -556,7 +556,7 @@ timestamp_abstime(PG_FUNCTION_ARGS) result = NOSTART_ABSTIME; else if (TIMESTAMP_IS_NOEND(timestamp)) result = NOEND_ABSTIME; - else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0) + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) { tz = DetermineLocalTimeZone(tm); result = tm2abstime(tm, tz); @@ -632,7 +632,7 @@ timestamptz_abstime(PG_FUNCTION_ARGS) result = NOSTART_ABSTIME; else if (TIMESTAMP_IS_NOEND(timestamp)) result = NOEND_ABSTIME; - else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0) + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) result = tm2abstime(tm, 0); else { diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 37308d7451..035a422bfc 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.125 2005/06/14 21:04:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.126 2005/06/15 00:34:09 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -142,7 +142,7 @@ timestamp_out(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) EncodeSpecialTimestamp(timestamp, buf); - else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0) + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf); else ereport(ERROR, @@ -178,7 +178,7 @@ timestamp_recv(PG_FUNCTION_ARGS) /* rangecheck: see if timestamp_out would like it */ if (TIMESTAMP_NOT_FINITE(timestamp)) /* ok */ ; - else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0) + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -381,7 +381,7 @@ timestamptz_out(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(dt)) EncodeSpecialTimestamp(dt, buf); - else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0) + else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn, NULL) == 0) EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf); else ereport(ERROR, @@ -419,7 +419,7 @@ timestamptz_recv(PG_FUNCTION_ARGS) /* rangecheck: see if timestamptz_out would like it */ if (TIMESTAMP_NOT_FINITE(timestamp)) /* ok */ ; - else if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0) + else if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -984,9 +984,12 @@ dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec) * Returns: * 0 on success * -1 on out of range + * + * If attimezone is NULL, the global timezone (including possblly brute forced + * timezone) will be used. */ int -timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char **tzn) +timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char **tzn, pg_tz *attimezone) { Timestamp date; Timestamp time; @@ -997,7 +1000,7 @@ timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char **tzn * specified. Go ahead and rotate to the local time zone since we will * later bypass any calls which adjust the tm fields. */ - if (HasCTZSet && (tzp != NULL)) + if ((attimezone==NULL) && HasCTZSet && (tzp != NULL)) { #ifdef HAVE_INT64_TIMESTAMP dt -= CTimeZone * USECS_PER_SEC; @@ -1050,7 +1053,7 @@ timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char **tzn * We have a brute force time zone per SQL99? Then use it without * change since we have already rotated to the time zone. */ - if (HasCTZSet) + if ((attimezone==NULL) && HasCTZSet) { *tzp = CTimeZone; tm->tm_isdst = 0; @@ -1081,7 +1084,7 @@ 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, global_timezone); + struct pg_tm *tx = pg_localtime(&utime, (attimezone!=NULL)?attimezone:global_timezone); tm->tm_year = tx->tm_year + 1900; tm->tm_mon = tx->tm_mon + 1; @@ -1926,7 +1929,7 @@ timestamp_pl_interval(PG_FUNCTION_ARGS) *tm = &tt; fsec_t fsec; - if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0) + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -2005,7 +2008,7 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS) *tm = &tt; fsec_t fsec; - if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0) + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -2332,8 +2335,8 @@ timestamp_age(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL) == 0 && - timestamp2tm(dt2, NULL, tm2, &fsec2, NULL) == 0) + if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL, NULL) == 0 && + timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0) { fsec = (fsec1 - fsec2); tm->tm_sec = tm1->tm_sec - tm2->tm_sec; @@ -2446,8 +2449,8 @@ timestamptz_age(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - if (timestamp2tm(dt1, &tz1, tm1, &fsec1, &tzn) == 0 && - timestamp2tm(dt2, &tz2, tm2, &fsec2, &tzn) == 0) + if (timestamp2tm(dt1, &tz1, tm1, &fsec1, &tzn, NULL) == 0 && + timestamp2tm(dt2, &tz2, tm2, &fsec2, &tzn, NULL) == 0) { fsec = fsec1 - fsec2; tm->tm_sec = tm1->tm_sec - tm2->tm_sec; @@ -2750,7 +2753,7 @@ timestamp_trunc(PG_FUNCTION_ARGS) if (type == UNITS) { - if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0) + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -2881,7 +2884,7 @@ timestamptz_trunc(PG_FUNCTION_ARGS) if (type == UNITS) { - if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0) + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3271,7 +3274,7 @@ timestamp_part(PG_FUNCTION_ARGS) if (type == UNITS) { - if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0) + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3405,7 +3408,7 @@ timestamp_part(PG_FUNCTION_ARGS) * convert to timestamptz to produce consistent * results */ - if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0) + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3425,7 +3428,7 @@ timestamp_part(PG_FUNCTION_ARGS) break; } case DTK_DOW: - if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0) + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3433,7 +3436,7 @@ timestamp_part(PG_FUNCTION_ARGS) break; case DTK_DOY: - if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0) + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3496,7 +3499,7 @@ timestamptz_part(PG_FUNCTION_ARGS) if (type == UNITS) { - if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0) + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3631,7 +3634,7 @@ timestamptz_part(PG_FUNCTION_ARGS) break; case DTK_DOW: - if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0) + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3639,7 +3642,7 @@ timestamptz_part(PG_FUNCTION_ARGS) break; case DTK_DOY: - if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0) + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3815,38 +3818,40 @@ timestamp_zone(PG_FUNCTION_ARGS) { text *zone = PG_GETARG_TEXT_P(0); Timestamp timestamp = PG_GETARG_TIMESTAMP(1); - TimestampTz result; + Timestamp result; int tz; - int type, - val; - char *lowzone; + pg_tz *tzp; + char tzname[TZ_STRLEN_MAX+1]; + int len; + struct pg_tm tm; + fsec_t fsec; if (TIMESTAMP_NOT_FINITE(timestamp)) PG_RETURN_TIMESTAMPTZ(timestamp); - lowzone = downcase_truncate_identifier(VARDATA(zone), - VARSIZE(zone) - VARHDRSZ, - false); - - type = DecodeSpecial(0, lowzone, &val); - - if (type == TZ || type == DTZ) - { - tz = -(val * 60); - - result = dt2local(timestamp, tz); - } - else - { + /* Find the specified timezone? */ + len = (VARSIZE(zone)-VARHDRSZ>TZ_STRLEN_MAX)?TZ_STRLEN_MAX:(VARSIZE(zone)-VARHDRSZ); + memcpy(tzname,VARDATA(zone),len); + tzname[len] = 0; + tzp = pg_tzset(tzname); + if (!tzp) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone \"%s\" not recognized", - lowzone))); - + errmsg("time zone \"%s\" not recognised", + tzname))); PG_RETURN_NULL(); } - PG_RETURN_TIMESTAMPTZ(result); + /* Apply the timezone change */ + if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0 || + tm2timestamp(&tm, fsec, NULL, &result) != 0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("could not convert to time zone \"%s\"", + tzname))); + PG_RETURN_NULL(); + } + PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(result)); } /* timestamp_zone() */ /* timestamp_izone() @@ -3906,7 +3911,7 @@ timestamp2timestamptz(Timestamp timestamp) else { - if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0) + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3941,7 +3946,7 @@ timestamptz_timestamp(PG_FUNCTION_ARGS) else { - if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0) + if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3950,7 +3955,6 @@ timestamptz_timestamp(PG_FUNCTION_ARGS) (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); } - PG_RETURN_TIMESTAMP(result); } @@ -3966,34 +3970,37 @@ timestamptz_zone(PG_FUNCTION_ARGS) Timestamp result; int tz; - int type, - val; - char *lowzone; + pg_tz *tzp; + char tzname[TZ_STRLEN_MAX]; + int len; + struct pg_tm tm; + fsec_t fsec = 0; if (TIMESTAMP_NOT_FINITE(timestamp)) PG_RETURN_NULL(); - lowzone = downcase_truncate_identifier(VARDATA(zone), - VARSIZE(zone) - VARHDRSZ, - false); + /* Find the specified zone */ + len = (VARSIZE(zone)-VARHDRSZ>TZ_STRLEN_MAX)?TZ_STRLEN_MAX:(VARSIZE(zone)-VARHDRSZ); + memcpy(tzname,VARDATA(zone),len); + tzname[len] = 0; + tzp = pg_tzset(tzname); - type = DecodeSpecial(0, lowzone, &val); - - if (type == TZ || type == DTZ) - { - tz = val * 60; - - result = dt2local(timestamp, tz); - } - else - { + if (!tzp) { ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone \"%s\" not recognized", lowzone))); + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("time zone \"%s\" not recognized", tzname))); PG_RETURN_NULL(); } + 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(); + } + PG_RETURN_TIMESTAMP(result); } /* timestamptz_zone() */ diff --git a/src/include/pgtime.h b/src/include/pgtime.h index b3322234e1..c36a871fb3 100644 --- a/src/include/pgtime.h +++ b/src/include/pgtime.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/include/pgtime.h,v 1.7 2005/04/19 03:13:59 momjian Exp $ + * $PostgreSQL: pgsql/src/include/pgtime.h,v 1.8 2005/06/15 00:34:09 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -57,4 +57,8 @@ extern bool tz_acceptable(pg_tz *tz); extern const char *pg_get_timezone_name(pg_tz *tz); extern pg_tz *global_timezone; + +/* Maximum length of a timezone name */ +#define TZ_STRLEN_MAX 255 + #endif /* _PGTIME_H */ diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h index 782b75ec8d..7475dacbe7 100644 --- a/src/include/utils/timestamp.h +++ b/src/include/utils/timestamp.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.44 2005/06/14 21:04:42 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.45 2005/06/15 00:34:10 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -264,7 +264,7 @@ extern TimestampTz StartTime; extern int tm2timestamp(struct pg_tm * tm, fsec_t fsec, int *tzp, Timestamp *dt); extern int timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, - fsec_t *fsec, char **tzn); + fsec_t *fsec, char **tzn, pg_tz *attimezone); extern void dt2time(Timestamp dt, int *hour, int *min, int *sec, fsec_t *fsec); extern int interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec); diff --git a/src/timezone/pgtz.h b/src/timezone/pgtz.h index e325dec483..a6e6bbf97f 100644 --- a/src/timezone/pgtz.h +++ b/src/timezone/pgtz.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/pgtz.h,v 1.11 2005/04/19 03:13:59 momjian Exp $ + * $PostgreSQL: pgsql/src/timezone/pgtz.h,v 1.12 2005/06/15 00:34:11 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -18,8 +18,6 @@ #include "tzfile.h" -#define TZ_STRLEN_MAX 255 - extern char *pg_TZDIR(void); #define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))