Commit 877f08da authored by Tom Lane's avatar Tom Lane

Fix up timetz input so that a date is required only when the specified

timezone actually has a daylight-savings rule.  This avoids breaking
cases that used to work because they went through the DecodePosixTimezone
code path.  Per contrib regression failures (mea culpa for not running
those yesterday...).  Also document the already-applied change to allow
GMT offsets up to 14 hours.
parent 723f716b
<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.178 2006/10/17 21:03:20 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.179 2006/10/18 16:43:13 tgl Exp $ -->
<chapter id="datatype"> <chapter id="datatype">
<title id="datatype-title">Data Types</title> <title id="datatype-title">Data Types</title>
...@@ -1356,8 +1356,8 @@ SELECT b, char_length(b) FROM test2; ...@@ -1356,8 +1356,8 @@ SELECT b, char_length(b) FROM test2;
<entry><type>time [ (<replaceable>p</replaceable>) ] with time zone</type></entry> <entry><type>time [ (<replaceable>p</replaceable>) ] with time zone</type></entry>
<entry>12 bytes</entry> <entry>12 bytes</entry>
<entry>times of day only, with time zone</entry> <entry>times of day only, with time zone</entry>
<entry>00:00:00+1359</entry> <entry>00:00:00+1459</entry>
<entry>24:00:00-1359</entry> <entry>24:00:00-1459</entry>
<entry>1 microsecond / 14 digits</entry> <entry>1 microsecond / 14 digits</entry>
</row> </row>
</tbody> </tbody>
...@@ -1594,7 +1594,8 @@ SELECT b, char_length(b) FROM test2; ...@@ -1594,7 +1594,8 @@ SELECT b, char_length(b) FROM test2;
and <xref linkend="datatype-timezone-table">.) If a time zone is and <xref linkend="datatype-timezone-table">.) If a time zone is
specified in the input for <type>time without time zone</type>, specified in the input for <type>time without time zone</type>,
it is silently ignored. You can also specify a date but it will it is silently ignored. You can also specify a date but it will
be ignored, except when you use a full time zone name like be ignored, except when you use a time zone name that involves a
daylight-savings rule, such as
<literal>America/New_York</literal>. In this case specifying the date <literal>America/New_York</literal>. In this case specifying the date
is required in order to determine whether standard or daylight-savings is required in order to determine whether standard or daylight-savings
time applies. The appropriate time zone offset is recorded in the time applies. The appropriate time zone offset is recorded in the
...@@ -1747,12 +1748,7 @@ SELECT b, char_length(b) FROM test2; ...@@ -1747,12 +1748,7 @@ SELECT b, char_length(b) FROM test2;
</programlisting> </programlisting>
are valid values, which follow the <acronym>ISO</acronym> 8601 are valid values, which follow the <acronym>ISO</acronym> 8601
standard. You can also specify the full time zone name as in standard. In addition, the wide-spread format
<programlisting>
1999-01-08 04:05:06 America/New_York
</programlisting>
In addition, the wide-spread format
<programlisting> <programlisting>
January 8 04:05:06 1999 PST January 8 04:05:06 1999 PST
</programlisting> </programlisting>
...@@ -2210,12 +2206,7 @@ January 8 04:05:06 1999 PST ...@@ -2210,12 +2206,7 @@ January 8 04:05:06 1999 PST
There is a conceptual and practical difference between the abbreviations There is a conceptual and practical difference between the abbreviations
and the full names: abbreviations always represent a fixed offset from and the full names: abbreviations always represent a fixed offset from
UTC, whereas most of the full names imply a local daylight-savings time UTC, whereas most of the full names imply a local daylight-savings time
rule and so have two possible UTC offsets. That's why you always have to rule and so have two possible UTC offsets.
specify a date if you want to use full time zone names in <type>timetz</>
values. This is also the reason why you should set <xref
linkend="guc-timezone"> to a full time zone name: this way,
<productname>PostgreSQL</productname>
will always know the correct UTC offset for your region.
</para> </para>
<para> <para>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.173 2006/10/17 21:03:21 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.174 2006/10/18 16:43:13 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1199,6 +1199,18 @@ DecodeDateTime(char **field, int *ftype, int nf, ...@@ -1199,6 +1199,18 @@ DecodeDateTime(char **field, int *ftype, int nf,
ptype = val; ptype = val;
break; break;
case UNKNOWN_FIELD:
/*
* Before giving up and declaring error, check to see
* if it is an all-alpha timezone name.
*/
namedTz = pg_tzset(field[i]);
if (!namedTz)
return DTERR_BAD_FORMAT;
/* we'll apply the zone setting below */
tmask = DTK_M(TZ);
break;
default: default:
return DTERR_BAD_FORMAT; return DTERR_BAD_FORMAT;
} }
...@@ -1911,6 +1923,18 @@ DecodeTimeOnly(char **field, int *ftype, int nf, ...@@ -1911,6 +1923,18 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
ptype = val; ptype = val;
break; break;
case UNKNOWN_FIELD:
/*
* Before giving up and declaring error, check to see
* if it is an all-alpha timezone name.
*/
namedTz = pg_tzset(field[i]);
if (!namedTz)
return DTERR_BAD_FORMAT;
/* we'll apply the zone setting below */
tmask = DTK_M(TZ);
break;
default: default:
return DTERR_BAD_FORMAT; return DTERR_BAD_FORMAT;
} }
...@@ -1952,19 +1976,29 @@ DecodeTimeOnly(char **field, int *ftype, int nf, ...@@ -1952,19 +1976,29 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
/* /*
* If we had a full timezone spec, compute the offset (we could not * If we had a full timezone spec, compute the offset (we could not
* do it before, because we need the date to resolve DST status). * do it before, because we may need the date to resolve DST status).
*/ */
if (namedTz != NULL) if (namedTz != NULL)
{ {
/* a date has to be specified */ long int gmtoff;
if ((fmask & DTK_DATE_M) != DTK_DATE_M)
return DTERR_BAD_FORMAT;
/* daylight savings time modifier disallowed with full TZ */ /* daylight savings time modifier disallowed with full TZ */
if (fmask & DTK_M(DTZMOD)) if (fmask & DTK_M(DTZMOD))
return DTERR_BAD_FORMAT; return DTERR_BAD_FORMAT;
/* if non-DST zone, we do not need to know the date */
if (pg_get_timezone_offset(namedTz, &gmtoff))
{
*tzp = -(int) gmtoff;
}
else
{
/* a date has to be specified */
if ((fmask & DTK_DATE_M) != DTK_DATE_M)
return DTERR_BAD_FORMAT;
*tzp = DetermineTimeZoneOffset(tm, namedTz); *tzp = DetermineTimeZoneOffset(tm, namedTz);
} }
}
/* timezone not specified? then find local timezone if possible */ /* timezone not specified? then find local timezone if possible */
if (tzp != NULL && !(fmask & DTK_M(TZ))) if (tzp != NULL && !(fmask & DTK_M(TZ)))
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/include/pgtime.h,v 1.13 2006/09/16 20:14:33 tgl Exp $ * $PostgreSQL: pgsql/src/include/pgtime.h,v 1.14 2006/10/18 16:43:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -55,6 +55,7 @@ extern size_t pg_strftime(char *s, size_t max, const char *format, ...@@ -55,6 +55,7 @@ extern size_t pg_strftime(char *s, size_t max, const char *format,
extern void pg_timezone_initialize(void); extern void pg_timezone_initialize(void);
extern pg_tz *pg_tzset(const char *tzname); extern pg_tz *pg_tzset(const char *tzname);
extern bool tz_acceptable(pg_tz *tz); extern bool tz_acceptable(pg_tz *tz);
extern bool pg_get_timezone_offset(const pg_tz *tz, long int *gmtoff);
extern const char *pg_get_timezone_name(pg_tz *tz); extern const char *pg_get_timezone_name(pg_tz *tz);
extern pg_tzenum *pg_tzenumerate_start(void); extern pg_tzenum *pg_tzenumerate_start(void);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.61 2006/09/16 20:14:33 tgl Exp $ * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.62 2006/10/18 16:43:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -176,7 +176,7 @@ ...@@ -176,7 +176,7 @@
#define DTK_DATE_M (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)) #define DTK_DATE_M (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY))
#define DTK_TIME_M (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_M(SECOND)) #define DTK_TIME_M (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_M(SECOND))
#define MAXDATELEN 51 /* maximum possible length of an input date #define MAXDATELEN 63 /* maximum possible length of an input date
* string (not counting tr. null) */ * string (not counting tr. null) */
#define MAXDATEFIELDS 25 /* maximum possible number of fields in a date #define MAXDATEFIELDS 25 /* maximum possible number of fields in a date
* string */ * string */
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.15 2006/10/16 19:58:26 tgl Exp $ * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.16 2006/10/18 16:43:14 tgl Exp $
*/ */
/* /*
...@@ -1066,6 +1066,31 @@ pg_next_dst_boundary(const pg_time_t *timep, ...@@ -1066,6 +1066,31 @@ pg_next_dst_boundary(const pg_time_t *timep,
return 1; return 1;
} }
/*
* If the given timezone uses only one GMT offset, store that offset
* into *gmtoff and return TRUE, else return FALSE.
*/
bool
pg_get_timezone_offset(const pg_tz *tz, long int *gmtoff)
{
/*
* The zone could have more than one ttinfo, if it's historically used
* more than one abbreviation. We return TRUE as long as they all have
* the same gmtoff.
*/
const struct state *sp;
int i;
sp = &tz->state;
for (i = 1; i < sp->typecnt; i++)
{
if (sp->ttis[i].tt_gmtoff != sp->ttis[0].tt_gmtoff)
return false;
}
*gmtoff = sp->ttis[0].tt_gmtoff;
return true;
}
/* /*
* Return the name of the current timezone * Return the name of the current timezone
*/ */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment