Refactor datetime functions' timezone lookup code to reduce duplication.
We already had five copies of essentially the same logic, and an upcoming patch introduces yet another use-case. That's past my threshold of pain, so introduce a common subroutine. There's not that much net code savings, but the chance of typos should go down. Inspired by a patch from Przemysław Sztoch, but different in detail. Discussion: https://postgr.es/m/01a84551-48dd-1359-bf7e-f6b0203a6bd0@sztoch.pl
This commit is contained in:
parent
cc1392d4aa
commit
3e59e5048d
@ -3052,38 +3052,23 @@ timetz_zone(PG_FUNCTION_ARGS)
|
|||||||
TimeTzADT *result;
|
TimeTzADT *result;
|
||||||
int tz;
|
int tz;
|
||||||
char tzname[TZ_STRLEN_MAX + 1];
|
char tzname[TZ_STRLEN_MAX + 1];
|
||||||
char *lowzone;
|
int type,
|
||||||
int dterr,
|
|
||||||
type,
|
|
||||||
val;
|
val;
|
||||||
pg_tz *tzp;
|
pg_tz *tzp;
|
||||||
DateTimeErrorExtra extra;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Look up the requested timezone. First we look in the timezone
|
* Look up the requested timezone.
|
||||||
* abbreviation table (to handle cases like "EST"), and if that fails, we
|
|
||||||
* look in the timezone database (to handle cases like
|
|
||||||
* "America/New_York"). (This matches the order in which timestamp input
|
|
||||||
* checks the cases; it's important because the timezone database unwisely
|
|
||||||
* uses a few zone names that are identical to offset abbreviations.)
|
|
||||||
*/
|
*/
|
||||||
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
|
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
|
||||||
|
|
||||||
/* DecodeTimezoneAbbrev requires lowercase input */
|
type = DecodeTimezoneName(tzname, &val, &tzp);
|
||||||
lowzone = downcase_truncate_identifier(tzname,
|
|
||||||
strlen(tzname),
|
|
||||||
false);
|
|
||||||
|
|
||||||
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
|
if (type == TZNAME_FIXED_OFFSET)
|
||||||
if (dterr)
|
|
||||||
DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (type == TZ || type == DTZ)
|
|
||||||
{
|
{
|
||||||
/* fixed-offset abbreviation */
|
/* fixed-offset abbreviation */
|
||||||
tz = -val;
|
tz = -val;
|
||||||
}
|
}
|
||||||
else if (type == DYNTZ)
|
else if (type == TZNAME_DYNTZ)
|
||||||
{
|
{
|
||||||
/* dynamic-offset abbreviation, resolve using transaction start time */
|
/* dynamic-offset abbreviation, resolve using transaction start time */
|
||||||
TimestampTz now = GetCurrentTransactionStartTimestamp();
|
TimestampTz now = GetCurrentTransactionStartTimestamp();
|
||||||
@ -3093,27 +3078,15 @@ timetz_zone(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* try it as a full zone name */
|
/* Get the offset-from-GMT that is valid now for the zone name */
|
||||||
tzp = pg_tzset(tzname);
|
TimestampTz now = GetCurrentTransactionStartTimestamp();
|
||||||
if (tzp)
|
struct pg_tm tm;
|
||||||
{
|
fsec_t fsec;
|
||||||
/* Get the offset-from-GMT that is valid now for the zone */
|
|
||||||
TimestampTz now = GetCurrentTransactionStartTimestamp();
|
|
||||||
struct pg_tm tm;
|
|
||||||
fsec_t fsec;
|
|
||||||
|
|
||||||
if (timestamp2tm(now, &tz, &tm, &fsec, NULL, tzp) != 0)
|
if (timestamp2tm(now, &tz, &tm, &fsec, NULL, tzp) != 0)
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range")));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
errmsg("time zone \"%s\" not recognized", tzname)));
|
errmsg("timestamp out of range")));
|
||||||
tz = 0; /* keep compiler quiet */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
|
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
|
#include "parser/scansup.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/date.h"
|
#include "utils/date.h"
|
||||||
#include "utils/datetime.h"
|
#include "utils/datetime.h"
|
||||||
@ -3162,6 +3163,90 @@ DecodeSpecial(int field, const char *lowtoken, int *val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* DecodeTimezoneName()
|
||||||
|
* Interpret string as a timezone abbreviation or name.
|
||||||
|
* Throw error if the name is not recognized.
|
||||||
|
*
|
||||||
|
* The return value indicates what kind of zone identifier it is:
|
||||||
|
* TZNAME_FIXED_OFFSET: fixed offset from UTC
|
||||||
|
* TZNAME_DYNTZ: dynamic timezone abbreviation
|
||||||
|
* TZNAME_ZONE: full tzdb zone name
|
||||||
|
*
|
||||||
|
* For TZNAME_FIXED_OFFSET, *offset receives the UTC offset (in seconds,
|
||||||
|
* with ISO sign convention: positive is east of Greenwich).
|
||||||
|
* For the other two cases, *tz receives the timezone struct representing
|
||||||
|
* the zone name or the abbreviation's underlying zone.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
DecodeTimezoneName(const char *tzname, int *offset, pg_tz **tz)
|
||||||
|
{
|
||||||
|
char *lowzone;
|
||||||
|
int dterr,
|
||||||
|
type;
|
||||||
|
DateTimeErrorExtra extra;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First we look in the timezone abbreviation table (to handle cases like
|
||||||
|
* "EST"), and if that fails, we look in the timezone database (to handle
|
||||||
|
* cases like "America/New_York"). This matches the order in which
|
||||||
|
* timestamp input checks the cases; it's important because the timezone
|
||||||
|
* database unwisely uses a few zone names that are identical to offset
|
||||||
|
* abbreviations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* DecodeTimezoneAbbrev requires lowercase input */
|
||||||
|
lowzone = downcase_truncate_identifier(tzname,
|
||||||
|
strlen(tzname),
|
||||||
|
false);
|
||||||
|
|
||||||
|
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, offset, tz, &extra);
|
||||||
|
if (dterr)
|
||||||
|
DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
if (type == TZ || type == DTZ)
|
||||||
|
{
|
||||||
|
/* fixed-offset abbreviation, return the offset */
|
||||||
|
return TZNAME_FIXED_OFFSET;
|
||||||
|
}
|
||||||
|
else if (type == DYNTZ)
|
||||||
|
{
|
||||||
|
/* dynamic-offset abbreviation, return its referenced timezone */
|
||||||
|
return TZNAME_DYNTZ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* try it as a full zone name */
|
||||||
|
*tz = pg_tzset(tzname);
|
||||||
|
if (*tz == NULL)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("time zone \"%s\" not recognized", tzname)));
|
||||||
|
return TZNAME_ZONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DecodeTimezoneNameToTz()
|
||||||
|
* Interpret string as a timezone abbreviation or name.
|
||||||
|
* Throw error if the name is not recognized.
|
||||||
|
*
|
||||||
|
* This is a simple wrapper for DecodeTimezoneName that produces a pg_tz *
|
||||||
|
* result in all cases.
|
||||||
|
*/
|
||||||
|
pg_tz *
|
||||||
|
DecodeTimezoneNameToTz(const char *tzname)
|
||||||
|
{
|
||||||
|
pg_tz *result;
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
if (DecodeTimezoneName(tzname, &offset, &result) == TZNAME_FIXED_OFFSET)
|
||||||
|
{
|
||||||
|
/* fixed-offset abbreviation, get a pg_tz descriptor for that */
|
||||||
|
result = pg_tzset_offset(-offset); /* flip to POSIX sign convention */
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ClearPgItmIn
|
/* ClearPgItmIn
|
||||||
*
|
*
|
||||||
* Zero out a pg_itm_in
|
* Zero out a pg_itm_in
|
||||||
|
@ -479,12 +479,7 @@ parse_sane_timezone(struct pg_tm *tm, text *zone)
|
|||||||
/*
|
/*
|
||||||
* Look up the requested timezone. First we try to interpret it as a
|
* Look up the requested timezone. First we try to interpret it as a
|
||||||
* numeric timezone specification; if DecodeTimezone decides it doesn't
|
* numeric timezone specification; if DecodeTimezone decides it doesn't
|
||||||
* like the format, we look in the timezone abbreviation table (to handle
|
* like the format, we try timezone abbreviations and names.
|
||||||
* cases like "EST"), and if that also fails, we look in the timezone
|
|
||||||
* database (to handle cases like "America/New_York"). (This matches the
|
|
||||||
* order in which timestamp input checks the cases; it's important because
|
|
||||||
* the timezone database unwisely uses a few zone names that are identical
|
|
||||||
* to offset abbreviations.)
|
|
||||||
*
|
*
|
||||||
* Note pg_tzset happily parses numeric input that DecodeTimezone would
|
* Note pg_tzset happily parses numeric input that DecodeTimezone would
|
||||||
* reject. To avoid having it accept input that would otherwise be seen
|
* reject. To avoid having it accept input that would otherwise be seen
|
||||||
@ -501,11 +496,9 @@ parse_sane_timezone(struct pg_tm *tm, text *zone)
|
|||||||
dterr = DecodeTimezone(tzname, &tz);
|
dterr = DecodeTimezone(tzname, &tz);
|
||||||
if (dterr != 0)
|
if (dterr != 0)
|
||||||
{
|
{
|
||||||
char *lowzone;
|
|
||||||
int type,
|
int type,
|
||||||
val;
|
val;
|
||||||
pg_tz *tzp;
|
pg_tz *tzp;
|
||||||
DateTimeErrorExtra extra;
|
|
||||||
|
|
||||||
if (dterr == DTERR_TZDISP_OVERFLOW)
|
if (dterr == DTERR_TZDISP_OVERFLOW)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -516,34 +509,22 @@ parse_sane_timezone(struct pg_tm *tm, text *zone)
|
|||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("time zone \"%s\" not recognized", tzname)));
|
errmsg("time zone \"%s\" not recognized", tzname)));
|
||||||
|
|
||||||
/* DecodeTimezoneAbbrev requires lowercase input */
|
type = DecodeTimezoneName(tzname, &val, &tzp);
|
||||||
lowzone = downcase_truncate_identifier(tzname,
|
|
||||||
strlen(tzname),
|
|
||||||
false);
|
|
||||||
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
|
|
||||||
if (dterr)
|
|
||||||
DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (type == TZ || type == DTZ)
|
if (type == TZNAME_FIXED_OFFSET)
|
||||||
{
|
{
|
||||||
/* fixed-offset abbreviation */
|
/* fixed-offset abbreviation */
|
||||||
tz = -val;
|
tz = -val;
|
||||||
}
|
}
|
||||||
else if (type == DYNTZ)
|
else if (type == TZNAME_DYNTZ)
|
||||||
{
|
{
|
||||||
/* dynamic-offset abbreviation, resolve using specified time */
|
/* dynamic-offset abbreviation, resolve using specified time */
|
||||||
tz = DetermineTimeZoneAbbrevOffset(tm, tzname, tzp);
|
tz = DetermineTimeZoneAbbrevOffset(tm, tzname, tzp);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* try it as a full zone name */
|
/* full zone name */
|
||||||
tzp = pg_tzset(tzname);
|
tz = DetermineTimeZoneOffset(tm, tzp);
|
||||||
if (tzp)
|
|
||||||
tz = DetermineTimeZoneOffset(tm, tzp);
|
|
||||||
else
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("time zone \"%s\" not recognized", tzname)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4304,12 +4285,7 @@ timestamptz_trunc_zone(PG_FUNCTION_ARGS)
|
|||||||
text *zone = PG_GETARG_TEXT_PP(2);
|
text *zone = PG_GETARG_TEXT_PP(2);
|
||||||
TimestampTz result;
|
TimestampTz result;
|
||||||
char tzname[TZ_STRLEN_MAX + 1];
|
char tzname[TZ_STRLEN_MAX + 1];
|
||||||
char *lowzone;
|
|
||||||
int dterr,
|
|
||||||
type,
|
|
||||||
val;
|
|
||||||
pg_tz *tzp;
|
pg_tz *tzp;
|
||||||
DateTimeErrorExtra extra;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* timestamptz_zone() doesn't look up the zone for infinite inputs, so we
|
* timestamptz_zone() doesn't look up the zone for infinite inputs, so we
|
||||||
@ -4319,37 +4295,11 @@ timestamptz_trunc_zone(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_TIMESTAMP(timestamp);
|
PG_RETURN_TIMESTAMP(timestamp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Look up the requested timezone (see notes in timestamptz_zone()).
|
* Look up the requested timezone.
|
||||||
*/
|
*/
|
||||||
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
|
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
|
||||||
|
|
||||||
/* DecodeTimezoneAbbrev requires lowercase input */
|
tzp = DecodeTimezoneNameToTz(tzname);
|
||||||
lowzone = downcase_truncate_identifier(tzname,
|
|
||||||
strlen(tzname),
|
|
||||||
false);
|
|
||||||
|
|
||||||
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
|
|
||||||
if (dterr)
|
|
||||||
DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (type == TZ || type == DTZ)
|
|
||||||
{
|
|
||||||
/* fixed-offset abbreviation, get a pg_tz descriptor for that */
|
|
||||||
tzp = pg_tzset_offset(-val);
|
|
||||||
}
|
|
||||||
else if (type == DYNTZ)
|
|
||||||
{
|
|
||||||
/* dynamic-offset abbreviation, use its referenced timezone */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* try it as a full zone name */
|
|
||||||
tzp = pg_tzset(tzname);
|
|
||||||
if (!tzp)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("time zone \"%s\" not recognized", tzname)));
|
|
||||||
}
|
|
||||||
|
|
||||||
result = timestamptz_trunc_internal(units, timestamp, tzp);
|
result = timestamptz_trunc_internal(units, timestamp, tzp);
|
||||||
|
|
||||||
@ -5429,12 +5379,9 @@ timestamp_zone(PG_FUNCTION_ARGS)
|
|||||||
TimestampTz result;
|
TimestampTz result;
|
||||||
int tz;
|
int tz;
|
||||||
char tzname[TZ_STRLEN_MAX + 1];
|
char tzname[TZ_STRLEN_MAX + 1];
|
||||||
char *lowzone;
|
int type,
|
||||||
int dterr,
|
|
||||||
type,
|
|
||||||
val;
|
val;
|
||||||
pg_tz *tzp;
|
pg_tz *tzp;
|
||||||
DateTimeErrorExtra extra;
|
|
||||||
struct pg_tm tm;
|
struct pg_tm tm;
|
||||||
fsec_t fsec;
|
fsec_t fsec;
|
||||||
|
|
||||||
@ -5442,31 +5389,19 @@ timestamp_zone(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_TIMESTAMPTZ(timestamp);
|
PG_RETURN_TIMESTAMPTZ(timestamp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Look up the requested timezone. First we look in the timezone
|
* Look up the requested timezone.
|
||||||
* abbreviation table (to handle cases like "EST"), and if that fails, we
|
|
||||||
* look in the timezone database (to handle cases like
|
|
||||||
* "America/New_York"). (This matches the order in which timestamp input
|
|
||||||
* checks the cases; it's important because the timezone database unwisely
|
|
||||||
* uses a few zone names that are identical to offset abbreviations.)
|
|
||||||
*/
|
*/
|
||||||
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
|
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
|
||||||
|
|
||||||
/* DecodeTimezoneAbbrev requires lowercase input */
|
type = DecodeTimezoneName(tzname, &val, &tzp);
|
||||||
lowzone = downcase_truncate_identifier(tzname,
|
|
||||||
strlen(tzname),
|
|
||||||
false);
|
|
||||||
|
|
||||||
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
|
if (type == TZNAME_FIXED_OFFSET)
|
||||||
if (dterr)
|
|
||||||
DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (type == TZ || type == DTZ)
|
|
||||||
{
|
{
|
||||||
/* fixed-offset abbreviation */
|
/* fixed-offset abbreviation */
|
||||||
tz = val;
|
tz = val;
|
||||||
result = dt2local(timestamp, tz);
|
result = dt2local(timestamp, tz);
|
||||||
}
|
}
|
||||||
else if (type == DYNTZ)
|
else if (type == TZNAME_DYNTZ)
|
||||||
{
|
{
|
||||||
/* dynamic-offset abbreviation, resolve using specified time */
|
/* dynamic-offset abbreviation, resolve using specified time */
|
||||||
if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
|
if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
|
||||||
@ -5478,28 +5413,16 @@ timestamp_zone(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* try it as a full zone name */
|
/* full zone name, rotate to that zone */
|
||||||
tzp = pg_tzset(tzname);
|
if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
|
||||||
if (tzp)
|
|
||||||
{
|
|
||||||
/* Apply the timezone change */
|
|
||||||
if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range")));
|
|
||||||
tz = DetermineTimeZoneOffset(&tm, tzp);
|
|
||||||
if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range")));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
errmsg("time zone \"%s\" not recognized", tzname)));
|
errmsg("timestamp out of range")));
|
||||||
result = 0; /* keep compiler quiet */
|
tz = DetermineTimeZoneOffset(&tm, tzp);
|
||||||
}
|
if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("timestamp out of range")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_VALID_TIMESTAMP(result))
|
if (!IS_VALID_TIMESTAMP(result))
|
||||||
@ -5687,42 +5610,27 @@ timestamptz_zone(PG_FUNCTION_ARGS)
|
|||||||
Timestamp result;
|
Timestamp result;
|
||||||
int tz;
|
int tz;
|
||||||
char tzname[TZ_STRLEN_MAX + 1];
|
char tzname[TZ_STRLEN_MAX + 1];
|
||||||
char *lowzone;
|
int type,
|
||||||
int dterr,
|
|
||||||
type,
|
|
||||||
val;
|
val;
|
||||||
pg_tz *tzp;
|
pg_tz *tzp;
|
||||||
DateTimeErrorExtra extra;
|
|
||||||
|
|
||||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
if (TIMESTAMP_NOT_FINITE(timestamp))
|
||||||
PG_RETURN_TIMESTAMP(timestamp);
|
PG_RETURN_TIMESTAMP(timestamp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Look up the requested timezone. First we look in the timezone
|
* Look up the requested timezone.
|
||||||
* abbreviation table (to handle cases like "EST"), and if that fails, we
|
|
||||||
* look in the timezone database (to handle cases like
|
|
||||||
* "America/New_York"). (This matches the order in which timestamp input
|
|
||||||
* checks the cases; it's important because the timezone database unwisely
|
|
||||||
* uses a few zone names that are identical to offset abbreviations.)
|
|
||||||
*/
|
*/
|
||||||
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
|
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
|
||||||
|
|
||||||
/* DecodeTimezoneAbbrev requires lowercase input */
|
type = DecodeTimezoneName(tzname, &val, &tzp);
|
||||||
lowzone = downcase_truncate_identifier(tzname,
|
|
||||||
strlen(tzname),
|
|
||||||
false);
|
|
||||||
|
|
||||||
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
|
if (type == TZNAME_FIXED_OFFSET)
|
||||||
if (dterr)
|
|
||||||
DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (type == TZ || type == DTZ)
|
|
||||||
{
|
{
|
||||||
/* fixed-offset abbreviation */
|
/* fixed-offset abbreviation */
|
||||||
tz = -val;
|
tz = -val;
|
||||||
result = dt2local(timestamp, tz);
|
result = dt2local(timestamp, tz);
|
||||||
}
|
}
|
||||||
else if (type == DYNTZ)
|
else if (type == TZNAME_DYNTZ)
|
||||||
{
|
{
|
||||||
/* dynamic-offset abbreviation, resolve using specified time */
|
/* dynamic-offset abbreviation, resolve using specified time */
|
||||||
int isdst;
|
int isdst;
|
||||||
@ -5732,30 +5640,18 @@ timestamptz_zone(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* try it as a full zone name */
|
/* full zone name, rotate from that zone */
|
||||||
tzp = pg_tzset(tzname);
|
struct pg_tm tm;
|
||||||
if (tzp)
|
fsec_t fsec;
|
||||||
{
|
|
||||||
/* Apply the timezone change */
|
|
||||||
struct pg_tm tm;
|
|
||||||
fsec_t fsec;
|
|
||||||
|
|
||||||
if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0)
|
if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0)
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range")));
|
|
||||||
if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range")));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
errmsg("time zone \"%s\" not recognized", tzname)));
|
errmsg("timestamp out of range")));
|
||||||
result = 0; /* keep compiler quiet */
|
if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
|
||||||
}
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("timestamp out of range")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_VALID_TIMESTAMP(result))
|
if (!IS_VALID_TIMESTAMP(result))
|
||||||
|
@ -295,6 +295,11 @@ typedef struct DateTimeErrorExtra
|
|||||||
const char *dtee_abbrev; /* relevant time zone abbreviation */
|
const char *dtee_abbrev; /* relevant time zone abbreviation */
|
||||||
} DateTimeErrorExtra;
|
} DateTimeErrorExtra;
|
||||||
|
|
||||||
|
/* Result codes for DecodeTimezoneName() */
|
||||||
|
#define TZNAME_FIXED_OFFSET 0
|
||||||
|
#define TZNAME_DYNTZ 1
|
||||||
|
#define TZNAME_ZONE 2
|
||||||
|
|
||||||
|
|
||||||
extern void GetCurrentDateTime(struct pg_tm *tm);
|
extern void GetCurrentDateTime(struct pg_tm *tm);
|
||||||
extern void GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp);
|
extern void GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp);
|
||||||
@ -340,6 +345,9 @@ extern int DecodeTimezoneAbbrev(int field, const char *lowtoken,
|
|||||||
extern int DecodeSpecial(int field, const char *lowtoken, int *val);
|
extern int DecodeSpecial(int field, const char *lowtoken, int *val);
|
||||||
extern int DecodeUnits(int field, const char *lowtoken, int *val);
|
extern int DecodeUnits(int field, const char *lowtoken, int *val);
|
||||||
|
|
||||||
|
extern int DecodeTimezoneName(const char *tzname, int *offset, pg_tz **tz);
|
||||||
|
extern pg_tz *DecodeTimezoneNameToTz(const char *tzname);
|
||||||
|
|
||||||
extern int j2day(int date);
|
extern int j2day(int date);
|
||||||
|
|
||||||
extern struct Node *TemporalSimplify(int32 max_precis, struct Node *node);
|
extern struct Node *TemporalSimplify(int32 max_precis, struct Node *node);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user