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.
This commit is contained in:
Tom Lane 2002-11-21 23:31:20 +00:00
parent ca1d1b79ab
commit 75394d3f5b
3 changed files with 195 additions and 53 deletions

View File

@ -1,5 +1,5 @@
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.108 2002/11/15 03:11:15 momjian Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.109 2002/11/21 23:31:20 tgl Exp $
--> -->
<chapter id="datatype"> <chapter id="datatype">
@ -1569,20 +1569,32 @@ SELECT b, char_length(b) FROM test2;
<secondary>data type</secondary> <secondary>data type</secondary>
</indexterm> </indexterm>
<indexterm>
<primary>timestamp with time zone</primary>
<secondary>data type</secondary>
</indexterm>
<indexterm> <indexterm>
<primary>timestamp without time zone</primary> <primary>timestamp without time zone</primary>
<secondary>data type</secondary> <secondary>data type</secondary>
</indexterm> </indexterm>
<para> <para>
Time stamp types exist as <type>timestamp [ The time stamp types are <type>timestamp [
(<replaceable>p</replaceable>) ]</type>, <type>timestamp [
(<replaceable>p</replaceable>) ] without time zone</type> and (<replaceable>p</replaceable>) ] without time zone</type> and
<type>timestamp [ (<replaceable>p</replaceable>) ] without time <type>timestamp [ (<replaceable>p</replaceable>) ] with time
zone</type>. A plain <type>timestamp</type> is equivalent to zone</type>. Writing just <type>timestamp</type> is equivalent to
<type>timestamp without timezone</type>. <type>timestamp without time zone</type>.
</para> </para>
<note>
<para>
Prior to <productname>PostgreSQL</productname> 7.3, writing just
<type>timestamp</type> was equivalent to <type>timestamp with time
zone</type>. This was changed for SQL spec compliance.
</para>
</note>
<para> <para>
Valid input for the time stamp types consists of a concatenation Valid input for the time stamp types consists of a concatenation
of a date and a time, followed by an optional of a date and a time, followed by an optional
@ -1615,11 +1627,38 @@ January 8 04:05:06 1999 PST
<para> <para>
For <type>timestamp without time zone</type>, any explicit time For <type>timestamp without time zone</type>, 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 resulting date/time value is derived from the explicit date/time
fields in the input value, and is not adjusted for time zone. fields in the input value, and is not adjusted for time zone.
</para> </para>
<para>
For <type>timestamp with time zone</type>, 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
<varname>TimeZone</> parameter, and is converted to UTC using the
offset for the <varname>TimeZone</> zone.
</para>
<para>
When a <type>timestamp with time
zone</type> value is output, it is always converted from UTC to the
current <varname>TimeZone</> zone, and displayed as local time in that
zone. To see the time in another time zone, either change
<varname>TimeZone</> or use the <literal>AT TIME ZONE</> construct
(see <xref linkend="functions-datetime-zoneconvert">).
</para>
<para>
Conversions between <type>timestamp without time zone</type> and
<type>timestamp with time zone</type> normally assume that the
<type>timestamp without time zone</type> value should be taken or given
as <varname>TimeZone</> local time. A different zone reference can
be specified for the conversion using <literal>AT TIME ZONE</>.
</para>
<table tocentry="1" id="datatype-timezone-table"> <table tocentry="1" id="datatype-timezone-table">
<title>Time Zone Input</title> <title>Time Zone Input</title>
<tgroup cols="2"> <tgroup cols="2">
@ -1707,24 +1746,28 @@ January 8 04:05:06 1999 PST
<para> <para>
The following <acronym>SQL</acronym>-compatible functions can be The following <acronym>SQL</acronym>-compatible functions can be
used as date or time used as date or time
input for the corresponding data type: <literal>CURRENT_DATE</literal>, values for the corresponding data type: <literal>CURRENT_DATE</literal>,
<literal>CURRENT_TIME</literal>, <literal>CURRENT_TIME</literal>,
<literal>CURRENT_TIMESTAMP</literal>. The latter two accept an <literal>CURRENT_TIMESTAMP</literal>. The latter two accept an
optional precision specification. (See also <xref linkend="functions-datetime">.) optional precision specification. (See also <xref linkend="functions-datetime-current">.)
</para> </para>
<para> <para>
<productname>PostgreSQL</productname> also supports several <productname>PostgreSQL</productname> also supports several
special constants for convenience, shown in <xref special date/time input values for convenience, as shown in <xref
linkend="datatype-datetime-special-table">. linkend="datatype-datetime-special-table">. The values
<literal>infinity</literal> and <literal>-infinity</literal>
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.
</para> </para>
<table id="datatype-datetime-special-table"> <table id="datatype-datetime-special-table">
<title>Special Date/Time Constants</title> <title>Special Date/Time Inputs</title>
<tgroup cols="2"> <tgroup cols="2">
<thead> <thead>
<row> <row>
<entry>Constant</entry> <entry>Input string</entry>
<entry>Description</entry> <entry>Description</entry>
</row> </row>
</thead> </thead>
@ -1735,15 +1778,13 @@ January 8 04:05:06 1999 PST
</row> </row>
<row> <row>
<entry><literal>infinity</literal></entry> <entry><literal>infinity</literal></entry>
<entry>later than other valid times</entry> <entry>later than all other timestamps (not available for
type <type>date</>)</entry>
</row> </row>
<row> <row>
<entry><literal>-infinity</literal></entry> <entry><literal>-infinity</literal></entry>
<entry>earlier than other valid times</entry> <entry>earlier than all other timestamps (not available for
</row> type <type>date</>)</entry>
<row>
<entry><literal>invalid</literal></entry>
<entry>illegal entry</entry>
</row> </row>
<row> <row>
<entry><literal>now</literal></entry> <entry><literal>now</literal></entry>
@ -1962,13 +2003,21 @@ January 8 04:05:06 1999 PST
</para> </para>
<para> <para>
There are several ways to affect the time-zone behavior: There are several ways to select the time zone used by the server:
<itemizedlist> <itemizedlist>
<listitem> <listitem>
<para> <para>
The <envar>TZ</envar> environment variable on the server host The <envar>TZ</envar> 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.
</para>
</listitem>
<listitem>
<para>
The <varname>timezone</varname> configuration parameter can be
set in <filename>postgresql.conf</>.
</para> </para>
</listitem> </listitem>
@ -1987,18 +2036,6 @@ January 8 04:05:06 1999 PST
sets the time zone for the session. sets the time zone for the session.
</para> </para>
</listitem> </listitem>
<listitem>
<para>
The construct
<programlisting>
<replaceable>timestamp</replaceable> AT TIME ZONE '<replaceable>zone</replaceable>'
</programlisting>
where <replaceable>zone</replaceable> can be specified as a
text time zone (e.g., <literal>'PST'</literal>) or as an
interval (e.g., <literal>INTERVAL '-08:00'</literal>).
</para>
</listitem>
</itemizedlist> </itemizedlist>
</para> </para>

View File

@ -1,5 +1,5 @@
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.130 2002/11/11 20:14:02 petere Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.131 2002/11/21 23:31:20 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
@ -3549,9 +3549,14 @@ SUBSTRING('foobar' FROM 'o(.)b') <lineannotation>o</lineannotation>
<literal>*</literal>, etc.). For formatting functions, refer to <literal>*</literal>, etc.). For formatting functions, refer to
<xref linkend="functions-formatting">. You should be familiar with <xref linkend="functions-formatting">. You should be familiar with
the background information on date/time data types (see <xref the background information on date/time data types (see <xref
linkend="datatype-datetime">). The date/time operators described linkend="datatype-datetime">).
below behave similarly for types involving time zones as well as </para>
those without.
<para>
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.
</para> </para>
<table id="operators-datetime-table"> <table id="operators-datetime-table">
@ -3771,7 +3776,7 @@ SUBSTRING('foobar' FROM 'o(.)b') <lineannotation>o</lineannotation>
<row> <row>
<entry><function>now</function>()</entry> <entry><function>now</function>()</entry>
<entry><type>timestamp</type></entry> <entry><type>timestamp with time zone</type></entry>
<entry>Current date and time (equivalent to <entry>Current date and time (equivalent to
<function>current_timestamp</function>); see <xref <function>current_timestamp</function>); see <xref
linkend="functions-datetime-current"> linkend="functions-datetime-current">
@ -3898,8 +3903,8 @@ SELECT EXTRACT(DOY FROM TIMESTAMP '2001-02-16 20:38:40');
<listitem> <listitem>
<para> <para>
For <type>date</type> and <type>timestamp</type> values, the For <type>date</type> and <type>timestamp</type> values, the
number of seconds since 1970-01-01 00:00:00-00 (Result may be number of seconds since 1970-01-01 00:00:00-00 (can be negative);
negative.); for <type>interval</type> values, the total number for <type>interval</type> values, the total number
of seconds in the interval of seconds in the interval
</para> </para>
@ -4122,12 +4127,12 @@ SELECT EXTRACT(YEAR FROM TIMESTAMP '2001-02-16 20:38:40');
<para> <para>
The <function>date_part</function> function is modeled on the traditional The <function>date_part</function> function is modeled on the traditional
<productname>Ingres</productname> equivalent to the <productname>Ingres</productname> equivalent to the
<acronym>SQL</acronym>-function <function>extract</function>: <acronym>SQL</acronym>-standard function <function>extract</function>:
<synopsis> <synopsis>
date_part('<replaceable>field</replaceable>', <replaceable>source</replaceable>) date_part('<replaceable>field</replaceable>', <replaceable>source</replaceable>)
</synopsis> </synopsis>
Note that here the <replaceable>field</replaceable> value needs to Note that here the <replaceable>field</replaceable> parameter needs to
be a string. The valid field values for be a string value, not a name. The valid field values for
<function>date_part</function> are the same as for <function>date_part</function> are the same as for
<function>extract</function>. <function>extract</function>.
</para> </para>
@ -4192,6 +4197,95 @@ SELECT date_trunc('year', TIMESTAMP '2001-02-16 20:38:40');
</para> </para>
</sect2> </sect2>
<sect2 id="functions-datetime-zoneconvert">
<title><function>AT TIME ZONE</function></title>
<indexterm>
<primary>timezone</primary>
<secondary>conversion</secondary>
</indexterm>
<para>
The <function>AT TIME ZONE</function> construct allows conversions
of timestamps to different timezones.
</para>
<table id="functions-datetime-zoneconvert-table">
<title>AT TIME ZONE Variants</title>
<tgroup cols="3">
<thead>
<row>
<entry>Expression</entry>
<entry>Returns</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<type>timestamp without time zone</type>
<literal>AT TIME ZONE</literal>
<replaceable>zone</>
</entry>
<entry><type>timestamp with time zone</type></entry>
<entry>Convert local time in given timezone to UTC</entry>
</row>
<row>
<entry>
<type>timestamp with time zone</type>
<literal>AT TIME ZONE</literal>
<replaceable>zone</>
</entry>
<entry><type>timestamp without time zone</type></entry>
<entry>Convert UTC to local time in given timezone</entry>
</row>
<row>
<entry>
<type>time with time zone</type>
<literal>AT TIME ZONE</literal>
<replaceable>zone</>
</entry>
<entry><type>time with time zone</type></entry>
<entry>Convert local time across timezones</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
In these expressions, the desired time <replaceable>zone</> can be
specified either as a text string (e.g., <literal>'PST'</literal>)
or as an interval (e.g., <literal>INTERVAL '-08:00'</literal>).
</para>
<para>
Examples (supposing that <varname>TimeZone</> is <literal>PST8PDT</>):
<screen>
SELECT TIMESTAMP '2001-02-16 20:38:40' AT TIME ZONE 'MST';
<lineannotation>Result: </lineannotation><computeroutput>2001-02-16 19:38:40-08</computeroutput>
SELECT TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40-05' AT TIME ZONE 'MST';
<lineannotation>Result: </lineannotation><computeroutput>2001-02-16 18:38:40</computeroutput>
</screen>
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).
</para>
<para>
The function <function>timezone</function>(<replaceable>zone</>,
<replaceable>timestamp</>) is equivalent to the SQL-compliant construct
<replaceable>timestamp</> <literal>AT TIME ZONE</literal>
<replaceable>zone</>.
</para>
</sect2>
<sect2 id="functions-datetime-current"> <sect2 id="functions-datetime-current">
<title>Current Date/Time</title> <title>Current Date/Time</title>
@ -4219,6 +4313,16 @@ LOCALTIMESTAMP
LOCALTIME ( <replaceable>precision</replaceable> ) LOCALTIME ( <replaceable>precision</replaceable> )
LOCALTIMESTAMP ( <replaceable>precision</replaceable> ) LOCALTIMESTAMP ( <replaceable>precision</replaceable> )
</synopsis> </synopsis>
</para>
<para>
<function>CURRENT_TIME</function> and
<function>CURRENT_TIMESTAMP</function> deliver values with time zone;
<function>LOCALTIME</function> and
<function>LOCALTIMESTAMP</function> deliver values without time zone.
</para>
<para>
<function>CURRENT_TIME</function>, <function>CURRENT_TIME</function>,
<function>CURRENT_TIMESTAMP</function>, <function>CURRENT_TIMESTAMP</function>,
<function>LOCALTIME</function>, and <function>LOCALTIME</function>, and

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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); text *zone = PG_GETARG_TEXT_P(0);
TimeTzADT *time = PG_GETARG_TIMETZADT_P(1); TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
TimeTzADT *result; TimeTzADT *result;
TimeADT time1;
int tz; int tz;
int type, int type,
val; val;
@ -2040,15 +2039,17 @@ timetz_zone(PG_FUNCTION_ARGS)
{ {
tz = val * 60; tz = val * 60;
#ifdef HAVE_INT64_TIMESTAMP #ifdef HAVE_INT64_TIMESTAMP
time1 = (time->time - ((time->zone + tz) * INT64CONST(1000000))); result->time = time->time + ((time->zone - tz) * INT64CONST(1000000));
result->time -= ((result->time / time1) * time1); while (result->time < INT64CONST(0))
if (result->time < INT64CONST(0))
result->time += INT64CONST(86400000000); result->time += INT64CONST(86400000000);
while (result->time >= INT64CONST(86400000000))
result->time -= INT64CONST(86400000000);
#else #else
time1 = (time->time - time->zone + tz); result->time = time->time + (time->zone - tz);
TMODULO(result->time, time1, 86400e0); while (result->time < 0)
if (result->time < 0)
result->time += 86400; result->time += 86400;
while (result->time >= 86400)
result->time -= 86400;
#endif #endif
result->zone = tz; result->zone = tz;
@ -2087,13 +2088,13 @@ timetz_izone(PG_FUNCTION_ARGS)
result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
#ifdef HAVE_INT64_TIMESTAMP #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)) while (result->time < INT64CONST(0))
result->time += INT64CONST(86400000000); result->time += INT64CONST(86400000000);
while (result->time >= INT64CONST(86400000000)) while (result->time >= INT64CONST(86400000000))
result->time -= INT64CONST(86400000000); result->time -= INT64CONST(86400000000);
#else #else
result->time = (time->time + (time->zone - tz)); result->time = time->time + (time->zone - tz);
while (result->time < 0) while (result->time < 0)
result->time += 86400; result->time += 86400;
while (result->time >= 86400) while (result->time >= 86400)