diff --git a/doc/src/sgml/datetime.sgml b/doc/src/sgml/datetime.sgml index 0e42e34fd69a67df6834b3012dbef3408231fab2..9610dc36c5cada51e4d6c12f98bb37c2fcf75e24 100644 --- a/doc/src/sgml/datetime.sgml +++ b/doc/src/sgml/datetime.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.45 2005/01/09 18:58:10 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.46 2005/06/15 00:34:08 momjian Exp $ --> <appendix id="datetime-appendix"> @@ -990,7 +990,9 @@ $PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.45 2005/01/09 18:58:10 tgl Exp <para> <xref linkend="datetime-timezone-set-table"> shows the time zone names recognized by <productname>PostgreSQL</productname> as valid - settings for the <xref linkend="guc-timezone"> parameter. Note that + settings for the <xref linkend="guc-timezone"> parameter, and as + 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 the names shown in <xref linkend="datetime-timezone-input-table">: 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 </para> <table id="datetime-timezone-set-table"> - <title>Time Zone Names for Setting <varname>timezone</></title> + <title>Time Zone Names for Setting <varname>timezone</> and <literal>AT TIME ZONE</></title> <tgroup cols="1"> <thead> <row> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 4fe5e1d6fca6ff6f20a23436c1d6090668d47087..38e2c4a7001b958b664136e3837459ab815a8543 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.256 2005/06/14 23:47:39 momjian Exp $ +$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.257 2005/06/15 00:34:08 momjian Exp $ PostgreSQL documentation --> @@ -5679,10 +5679,7 @@ SELECT date_trunc('year', TIMESTAMP '2001-02-16 20:38:40'); specified either as a text string (e.g., <literal>'PST'</literal>) or as an interval (e.g., <literal>INTERVAL '-08:00'</literal>). In the text case, the available zone names are those shown in - <xref linkend="datetime-timezone-input-table">. (It would be useful - to support the more general names shown in - <xref linkend="datetime-timezone-set-table">, but this is not yet - implemented.) + <xref linkend="datetime-timezone-set-table">. </para> <para> diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 5371c64250a7b24f5763c3a108a174f5dd5ba477..abc6155594f137f242268fb8deff5d36c26da894 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 <ctype.h> #include <limits.h> #include <float.h> +#include <time.h> #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,53 +2464,60 @@ 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; - - lowzone = downcase_truncate_identifier(VARDATA(zone), - VARSIZE(zone) - VARHDRSZ, - false); - - type = DecodeSpecial(0, lowzone, &val); + char tzname[TZ_STRLEN_MAX]; + int len; + pg_tz *tzp; + struct pg_tm *tm; + pg_time_t now; + + /* 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", tzname))); + PG_RETURN_NULL(); + } - result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); + /* 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(); + } - if (type == TZ || type == DTZ) - { - tz = val * 60; + result = (TimeTzADT *)palloc(sizeof(TimeTzADT)); + + tz = -tm->tm_gmtoff; #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; + 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 = 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; + 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; - } - else - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone \"%s\" not recognized", lowzone))); - - PG_RETURN_NULL(); - } + 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 68a73bbb6fc793866818e41bf8e6c6f08619ec9f..e4de64444eae73e631f6f4d069fa28aef825973f 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 bebd3f9d3256096719094b43502e3cf8341cd2ef..d712d9f8b46e42b9118bd9cae39381b0894b8236 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 37308d7451a769316f4040e65103a77744d0c899..035a422bfcc33361afb4ef2769e4107c434a7096 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,31 +3970,34 @@ 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); - - type = DecodeSpecial(0, lowzone, &val); + /* 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); - if (type == TZ || type == DTZ) - { - tz = val * 60; + if (!tzp) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("time zone \"%s\" not recognized", tzname))); - result = dt2local(timestamp, tz); + PG_RETURN_NULL(); } - else - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone \"%s\" not recognized", lowzone))); + 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(); } diff --git a/src/include/pgtime.h b/src/include/pgtime.h index b3322234e19ce26939d09f6c4ab9f7a0ef824151..c36a871fb33c6aca3156d836ffcb9d5448a93e76 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 782b75ec8dfd5e01d45623e1bd9d36687ab808c5..7475dacbe7c0532cbff949076f813bdeb71f2cd6 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 e325dec48328e3125f3a9cd49309956c8b2cd5ad..a6e6bbf97f61a965376208fdfc92821b51fc6e52 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))