From 75394d3f5b1fcffc5dc9a1fc95b7b57c034ba020 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 21 Nov 2002 23:31:20 +0000 Subject: [PATCH] Fix breakage in new-in-7.3 timetz_zone() function: was giving random results due to doing arithmetic on uninitialized values. Add some documentation about the AT TIME ZONE construct. Update some other date/time documentation that seemed out of date for 7.3. --- doc/src/sgml/datatype.sgml | 103 +++++++++++++++++++---------- doc/src/sgml/func.sgml | 124 ++++++++++++++++++++++++++++++++--- src/backend/utils/adt/date.c | 21 +++--- 3 files changed, 195 insertions(+), 53 deletions(-) diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 1c07a9c5f1..cac65623dc 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -1,5 +1,5 @@ @@ -1569,20 +1569,32 @@ SELECT b, char_length(b) FROM test2; data type + + timestamp with time zone + data type + + timestamp without time zone data type - Time stamp types exist as timestamp [ - (p) ], timestamp [ + The time stamp types are timestamp [ (p) ] without time zone and - timestamp [ (p) ] without time - zone. A plain timestamp is equivalent to - timestamp without timezone. + timestamp [ (p) ] with time + zone. Writing just timestamp is equivalent to + timestamp without time zone. + + + Prior to PostgreSQL 7.3, writing just + timestamp was equivalent to timestamp with time + zone. This was changed for SQL spec compliance. + + + Valid input for the time stamp types consists of a concatenation of a date and a time, followed by an optional @@ -1615,11 +1627,38 @@ January 8 04:05:06 1999 PST For timestamp without time zone, any explicit time - zone specified in the input is silently swallowed. That is, the + zone specified in the input is silently ignored. That is, the resulting date/time value is derived from the explicit date/time fields in the input value, and is not adjusted for time zone. + + For timestamp with time zone, the internally stored + value is always in UTC (GMT). An input value that has an explicit + time zone specified is converted to UTC using the appropriate offset + for that time zone. If no time zone is stated in the input string, + then it is assumed to be in the time zone indicated by the system's + TimeZone parameter, and is converted to UTC using the + offset for the TimeZone zone. + + + + When a timestamp with time + zone value is output, it is always converted from UTC to the + current TimeZone zone, and displayed as local time in that + zone. To see the time in another time zone, either change + TimeZone or use the AT TIME ZONE construct + (see ). + + + + Conversions between timestamp without time zone and + timestamp with time zone normally assume that the + timestamp without time zone value should be taken or given + as TimeZone local time. A different zone reference can + be specified for the conversion using AT TIME ZONE. + + Time Zone Input @@ -1707,24 +1746,28 @@ January 8 04:05:06 1999 PST The following SQL-compatible functions can be used as date or time - input for the corresponding data type: CURRENT_DATE, + values for the corresponding data type: CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP. The latter two accept an - optional precision specification. (See also .) + optional precision specification. (See also .) PostgreSQL also supports several - special constants for convenience, shown in . + special date/time input values for convenience, as shown in . The values + infinity and -infinity + are specially represented inside the system and will be displayed + the same way; but the others are simply notational shorthands + that will be converted to ordinary date/time values when read.
- Special Date/Time Constants + Special Date/Time Inputs - Constant + Input string Description @@ -1735,15 +1778,13 @@ January 8 04:05:06 1999 PST infinity - later than other valid times + later than all other timestamps (not available for + type date) -infinity - earlier than other valid times - - - invalid - illegal entry + earlier than all other timestamps (not available for + type date) now @@ -1962,13 +2003,21 @@ January 8 04:05:06 1999 PST - There are several ways to affect the time-zone behavior: + There are several ways to select the time zone used by the server: The TZ environment variable on the server host - is used by the server as the default time zone. + is used by the server as the default time zone, if no other is + specified. + + + + + + The timezone configuration parameter can be + set in postgresql.conf. @@ -1987,18 +2036,6 @@ January 8 04:05:06 1999 PST sets the time zone for the session. - - - - The construct - -timestamp AT TIME ZONE 'zone' - - where zone can be specified as a - text time zone (e.g., 'PST') or as an - interval (e.g., INTERVAL '-08:00'). - - diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 7174194845..7443f001ef 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,5 +1,5 @@ @@ -3549,9 +3549,14 @@ SUBSTRING('foobar' FROM 'o(.)b') o *, etc.). For formatting functions, refer to . You should be familiar with the background information on date/time data types (see ). The date/time operators described - below behave similarly for types involving time zones as well as - those without. + linkend="datatype-datetime">). + + + + All the functions and operators described below that take time or timestamp + inputs actually come in two variants: one that takes time or timestamp + with time zone, and one that takes time or timestamp without time zone. + For brevity, these variants are not shown separately.
@@ -3771,7 +3776,7 @@ SUBSTRING('foobar' FROM 'o(.)b') o now() - timestamp + timestamp with time zone Current date and time (equivalent to current_timestamp); see @@ -3898,8 +3903,8 @@ SELECT EXTRACT(DOY FROM TIMESTAMP '2001-02-16 20:38:40'); For date and timestamp values, the - number of seconds since 1970-01-01 00:00:00-00 (Result may be - negative.); for interval values, the total number + number of seconds since 1970-01-01 00:00:00-00 (can be negative); + for interval values, the total number of seconds in the interval @@ -4122,12 +4127,12 @@ SELECT EXTRACT(YEAR FROM TIMESTAMP '2001-02-16 20:38:40'); The date_part function is modeled on the traditional Ingres equivalent to the - SQL-function extract: + SQL-standard function extract: date_part('field', source) - Note that here the field value needs to - be a string. The valid field values for + Note that here the field parameter needs to + be a string value, not a name. The valid field values for date_part are the same as for extract. @@ -4192,6 +4197,95 @@ SELECT date_trunc('year', TIMESTAMP '2001-02-16 20:38:40'); + + <function>AT TIME ZONE</function> + + + timezone + conversion + + + + The AT TIME ZONE construct allows conversions + of timestamps to different timezones. + + +
+ AT TIME ZONE Variants + + + + Expression + Returns + Description + + + + + + + + timestamp without time zone + AT TIME ZONE + zone + + timestamp with time zone + Convert local time in given timezone to UTC + + + + + timestamp with time zone + AT TIME ZONE + zone + + timestamp without time zone + Convert UTC to local time in given timezone + + + + + time with time zone + AT TIME ZONE + zone + + time with time zone + Convert local time across timezones + + + + +
+ + + In these expressions, the desired time zone can be + specified either as a text string (e.g., 'PST') + or as an interval (e.g., INTERVAL '-08:00'). + + + + Examples (supposing that TimeZone is PST8PDT): + +SELECT TIMESTAMP '2001-02-16 20:38:40' AT TIME ZONE 'MST'; +Result: 2001-02-16 19:38:40-08 + +SELECT TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40-05' AT TIME ZONE 'MST'; +Result: 2001-02-16 18:38:40 + + The first example takes a zone-less timestamp and interprets it as MST time + (GMT-7) to produce a UTC timestamp, which is then rotated to PST (GMT-8) + for display. The second example takes a timestamp specified in EST + (GMT-5) and converts it to local time in MST (GMT-7). + + + + The function timezone(zone, + timestamp) is equivalent to the SQL-compliant construct + timestamp AT TIME ZONE + zone. + + + Current Date/Time @@ -4219,6 +4313,16 @@ LOCALTIMESTAMP LOCALTIME ( precision ) LOCALTIMESTAMP ( precision ) +
+ + + CURRENT_TIME and + CURRENT_TIMESTAMP deliver values with time zone; + LOCALTIME and + LOCALTIMESTAMP deliver values without time zone. + + + CURRENT_TIME, CURRENT_TIMESTAMP, LOCALTIME, and diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 6c35f3ae00..3b92125889 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.73 2002/09/21 19:52:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.74 2002/11/21 23:31:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2013,7 +2013,6 @@ timetz_zone(PG_FUNCTION_ARGS) text *zone = PG_GETARG_TEXT_P(0); TimeTzADT *time = PG_GETARG_TIMETZADT_P(1); TimeTzADT *result; - TimeADT time1; int tz; int type, val; @@ -2040,15 +2039,17 @@ timetz_zone(PG_FUNCTION_ARGS) { tz = val * 60; #ifdef HAVE_INT64_TIMESTAMP - time1 = (time->time - ((time->zone + tz) * INT64CONST(1000000))); - result->time -= ((result->time / time1) * time1); - if (result->time < INT64CONST(0)) + result->time = time->time + ((time->zone - tz) * INT64CONST(1000000)); + while (result->time < INT64CONST(0)) result->time += INT64CONST(86400000000); + while (result->time >= INT64CONST(86400000000)) + result->time -= INT64CONST(86400000000); #else - time1 = (time->time - time->zone + tz); - TMODULO(result->time, time1, 86400e0); - if (result->time < 0) + result->time = time->time + (time->zone - tz); + while (result->time < 0) result->time += 86400; + while (result->time >= 86400) + result->time -= 86400; #endif result->zone = tz; @@ -2087,13 +2088,13 @@ timetz_izone(PG_FUNCTION_ARGS) result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); #ifdef HAVE_INT64_TIMESTAMP - result->time = (time->time + ((time->zone - tz) * INT64CONST(1000000))); + result->time = time->time + ((time->zone - tz) * INT64CONST(1000000)); while (result->time < INT64CONST(0)) result->time += INT64CONST(86400000000); while (result->time >= INT64CONST(86400000000)) result->time -= INT64CONST(86400000000); #else - result->time = (time->time + (time->zone - tz)); + result->time = time->time + (time->zone - tz); while (result->time < 0) result->time += 86400; while (result->time >= 86400)