TimeTzADT *result;
int tz;
char tzname[TZ_STRLEN_MAX + 1];
- char *lowzone;
- int dterr,
- type,
+ int type,
val;
pg_tz *tzp;
- DateTimeErrorExtra extra;
/*
- * Look up the requested timezone. 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.)
+ * Look up the requested timezone.
*/
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
- /* DecodeTimezoneAbbrev requires lowercase input */
- lowzone = downcase_truncate_identifier(tzname,
- strlen(tzname),
- false);
+ type = DecodeTimezoneName(tzname, &val, &tzp);
- 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 */
tz = -val;
}
- else if (type == DYNTZ)
+ else if (type == TZNAME_DYNTZ)
{
/* dynamic-offset abbreviation, resolve using transaction start time */
TimestampTz now = GetCurrentTransactionStartTimestamp();
}
else
{
- /* try it as a full zone name */
- tzp = pg_tzset(tzname);
- if (tzp)
- {
- /* Get the offset-from-GMT that is valid now for the zone */
- TimestampTz now = GetCurrentTransactionStartTimestamp();
- struct pg_tm tm;
- fsec_t fsec;
+ /* Get the offset-from-GMT that is valid now for the zone name */
+ TimestampTz now = GetCurrentTransactionStartTimestamp();
+ struct pg_tm tm;
+ fsec_t fsec;
- if (timestamp2tm(now, &tz, &tm, &fsec, NULL, tzp) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timestamp out of range")));
- }
- else
- {
+ if (timestamp2tm(now, &tz, &tm, &fsec, NULL, tzp) != 0)
ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("time zone \"%s\" not recognized", tzname)));
- tz = 0; /* keep compiler quiet */
- }
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
}
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
+#include "parser/scansup.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
}
+/* 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
*
* Zero out a pg_itm_in
/*
* Look up the requested timezone. First we try to interpret it as a
* numeric timezone specification; if DecodeTimezone decides it doesn't
- * like the format, we look in the timezone abbreviation table (to handle
- * 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.)
+ * like the format, we try timezone abbreviations and names.
*
* Note pg_tzset happily parses numeric input that DecodeTimezone would
* reject. To avoid having it accept input that would otherwise be seen
dterr = DecodeTimezone(tzname, &tz);
if (dterr != 0)
{
- char *lowzone;
int type,
val;
pg_tz *tzp;
- DateTimeErrorExtra extra;
if (dterr == DTERR_TZDISP_OVERFLOW)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("time zone \"%s\" not recognized", tzname)));
- /* DecodeTimezoneAbbrev requires lowercase input */
- lowzone = downcase_truncate_identifier(tzname,
- strlen(tzname),
- false);
- dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
- if (dterr)
- DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
+ type = DecodeTimezoneName(tzname, &val, &tzp);
- if (type == TZ || type == DTZ)
+ if (type == TZNAME_FIXED_OFFSET)
{
/* fixed-offset abbreviation */
tz = -val;
}
- else if (type == DYNTZ)
+ else if (type == TZNAME_DYNTZ)
{
/* dynamic-offset abbreviation, resolve using specified time */
tz = DetermineTimeZoneAbbrevOffset(tm, tzname, tzp);
}
else
{
- /* try it as a full zone name */
- tzp = pg_tzset(tzname);
- if (tzp)
- tz = DetermineTimeZoneOffset(tm, tzp);
- else
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("time zone \"%s\" not recognized", tzname)));
+ /* full zone name */
+ tz = DetermineTimeZoneOffset(tm, tzp);
}
}
text *zone = PG_GETARG_TEXT_PP(2);
TimestampTz result;
char tzname[TZ_STRLEN_MAX + 1];
- char *lowzone;
- int dterr,
- type,
- val;
pg_tz *tzp;
- DateTimeErrorExtra extra;
/*
* timestamptz_zone() doesn't look up the zone for infinite inputs, so we
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));
- /* DecodeTimezoneAbbrev requires lowercase input */
- 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)));
- }
+ tzp = DecodeTimezoneNameToTz(tzname);
result = timestamptz_trunc_internal(units, timestamp, tzp);
TimestampTz result;
int tz;
char tzname[TZ_STRLEN_MAX + 1];
- char *lowzone;
- int dterr,
- type,
+ int type,
val;
pg_tz *tzp;
- DateTimeErrorExtra extra;
struct pg_tm tm;
fsec_t fsec;
PG_RETURN_TIMESTAMPTZ(timestamp);
/*
- * Look up the requested timezone. 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.)
+ * Look up the requested timezone.
*/
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
- /* DecodeTimezoneAbbrev requires lowercase input */
- lowzone = downcase_truncate_identifier(tzname,
- strlen(tzname),
- false);
+ type = DecodeTimezoneName(tzname, &val, &tzp);
- 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 */
tz = val;
result = dt2local(timestamp, tz);
}
- else if (type == DYNTZ)
+ else if (type == TZNAME_DYNTZ)
{
/* dynamic-offset abbreviation, resolve using specified time */
if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
}
else
{
- /* try it as a full zone name */
- tzp = pg_tzset(tzname);
- 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
- {
+ /* full zone name, rotate to that zone */
+ if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("time zone \"%s\" not recognized", tzname)));
- result = 0; /* keep compiler quiet */
- }
+ (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")));
}
if (!IS_VALID_TIMESTAMP(result))
Timestamp result;
int tz;
char tzname[TZ_STRLEN_MAX + 1];
- char *lowzone;
- int dterr,
- type,
+ int type,
val;
pg_tz *tzp;
- DateTimeErrorExtra extra;
if (TIMESTAMP_NOT_FINITE(timestamp))
PG_RETURN_TIMESTAMP(timestamp);
/*
- * Look up the requested timezone. 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.)
+ * Look up the requested timezone.
*/
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
- /* DecodeTimezoneAbbrev requires lowercase input */
- lowzone = downcase_truncate_identifier(tzname,
- strlen(tzname),
- false);
-
- dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
- if (dterr)
- DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
+ type = DecodeTimezoneName(tzname, &val, &tzp);
- if (type == TZ || type == DTZ)
+ if (type == TZNAME_FIXED_OFFSET)
{
/* fixed-offset abbreviation */
tz = -val;
result = dt2local(timestamp, tz);
}
- else if (type == DYNTZ)
+ else if (type == TZNAME_DYNTZ)
{
/* dynamic-offset abbreviation, resolve using specified time */
int isdst;
}
else
{
- /* try it as a full zone name */
- tzp = pg_tzset(tzname);
- if (tzp)
- {
- /* Apply the timezone change */
- struct pg_tm tm;
- fsec_t fsec;
+ /* full zone name, rotate from that zone */
+ struct pg_tm tm;
+ fsec_t fsec;
- 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
- {
+ if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0)
ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("time zone \"%s\" not recognized", tzname)));
- result = 0; /* keep compiler quiet */
- }
+ (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")));
}
if (!IS_VALID_TIMESTAMP(result))