70
75
*****************************************************************************/
73
* Definitions for squeezing values into "value"
74
* We set aside a high bit for a sign, and scale the timezone offsets
75
* in minutes by a factor of 15 (so can represent quarter-hour increments).
77
#define ABS_SIGNBIT ((char) 0200)
78
#define VALMASK ((char) 0177)
80
#define NEG(n) ((n)|ABS_SIGNBIT)
81
#define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
82
#define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 15) /* uncompress */
83
#define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/15): POS(v)/15))
86
78
* datetktbl holds date/time keywords.
88
80
* Note that this table must be strictly alphabetically ordered to allow an
89
81
* O(ln(N)) search algorithm to be used.
91
* The token field is NOT guaranteed to be NULL-terminated.
93
* To keep this table reasonably small, we divide the value for TZ and DTZ
94
* entries by 15 (so they are on 15 minute boundaries) and truncate the token
95
* field at TOKMAXLEN characters.
96
* Formerly, we divided by 10 rather than 15 but there are a few time zones
97
* which are 30 or 45 minutes away from an even hour, most are on an hour
98
* boundary, and none on other boundaries.
100
* The static table contains no TZ or DTZ entries, rather those are loaded
101
* from configuration files and stored in timezonetktbl, which has the same
102
* format as the static datetktbl.
83
* The token field must be NUL-terminated; we truncate entries to TOKMAXLEN
86
* The static table contains no TZ, DTZ, or DYNTZ entries; rather those
87
* are loaded from configuration files and stored in zoneabbrevtbl, whose
88
* abbrevs[] field has the same format as the static datetktbl.
104
static datetkn *timezonetktbl = NULL;
106
static int sztimezonetktbl = 0;
108
90
static const datetkn datetktbl[] = {
109
91
/* token, type, value */
110
92
{EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
1440
1457
/* DetermineTimeZoneOffset()
1442
* Given a struct pg_tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and
1443
* tm_sec fields are set, attempt to determine the applicable time zone
1444
* (ie, regular or daylight-savings time) at that time. Set the struct pg_tm's
1445
* tm_isdst field accordingly, and return the actual timezone offset.
1459
* Given a struct pg_tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min,
1460
* and tm_sec fields are set, and a zic-style time zone definition, determine
1461
* the applicable GMT offset and daylight-savings status at that time.
1462
* Set the struct pg_tm's tm_isdst field accordingly, and return the GMT
1463
* offset as the function result.
1465
* Note: if the date is out of the range we can deal with, we return zero
1466
* as the GMT offset and set tm_isdst = 0. We don't throw an error here,
1467
* though probably some higher-level code will.
1470
DetermineTimeZoneOffset(struct pg_tm * tm, pg_tz *tzp)
1474
return DetermineTimeZoneOffsetInternal(tm, tzp, &t);
1478
/* DetermineTimeZoneOffsetInternal()
1480
* As above, but also return the actual UTC time imputed to the date/time
1483
* In event of an out-of-range date, we punt by returning zero into *tp.
1484
* This is okay for the immediate callers but is a good reason for not
1485
* exposing this worker function globally.
1447
1487
* Note: it might seem that we should use mktime() for this, but bitter
1448
1488
* experience teaches otherwise. This code is much faster than most versions
1449
1489
* of mktime(), anyway.
1452
DetermineTimeZoneOffset(struct pg_tm * tm, pg_tz *tzp)
1492
DetermineTimeZoneOffsetInternal(struct pg_tm * tm, pg_tz *tzp, pg_time_t *tp)
1529
* If both before or both after the boundary time, we know what to do
1570
* If both before or both after the boundary time, we know what to do. The
1571
* boundary time itself is considered to be after the transition, which
1572
* means we can accept aftertime == boundary in the second case.
1531
if (beforetime <= boundary && aftertime < boundary)
1574
if (beforetime < boundary && aftertime < boundary)
1533
1576
tm->tm_isdst = before_isdst;
1534
1578
return -(int) before_gmtoff;
1536
1580
if (beforetime > boundary && aftertime >= boundary)
1538
1582
tm->tm_isdst = after_isdst;
1539
1584
return -(int) after_gmtoff;
1543
* It's an invalid or ambiguous time due to timezone transition. Prefer
1544
* the standard-time interpretation.
1588
* It's an invalid or ambiguous time due to timezone transition. In a
1589
* spring-forward transition, prefer the "before" interpretation; in a
1590
* fall-back transition, prefer "after". (We used to define and implement
1591
* this test as "prefer the standard-time interpretation", but that rule
1592
* does not help to resolve the behavior when both times are reported as
1593
* standard time; which does happen, eg Europe/Moscow in Oct 2014.)
1546
if (after_isdst == 0)
1595
if (beforetime > aftertime)
1548
tm->tm_isdst = after_isdst;
1549
return -(int) after_gmtoff;
1597
tm->tm_isdst = before_isdst;
1599
return -(int) before_gmtoff;
1551
tm->tm_isdst = before_isdst;
1552
return -(int) before_gmtoff;
1601
tm->tm_isdst = after_isdst;
1603
return -(int) after_gmtoff;
1555
1606
/* Given date is out of range, so assume UTC */
1556
1607
tm->tm_isdst = 0;
1613
/* DetermineTimeZoneAbbrevOffset()
1615
* Determine the GMT offset and DST flag to be attributed to a dynamic
1616
* time zone abbreviation, that is one whose meaning has changed over time.
1617
* *tm contains the local time at which the meaning should be determined,
1618
* and tm->tm_isdst receives the DST flag.
1620
* This differs from the behavior of DetermineTimeZoneOffset() in that a
1621
* standard-time or daylight-time abbreviation forces use of the corresponding
1622
* GMT offset even when the zone was then in DS or standard time respectively.
1625
DetermineTimeZoneAbbrevOffset(struct pg_tm * tm, const char *abbr, pg_tz *tzp)
1630
* Compute the UTC time we want to probe at. (In event of overflow, we'll
1631
* probe at the epoch, which is a bit random but probably doesn't matter.)
1633
(void) DetermineTimeZoneOffsetInternal(tm, tzp, &t);
1635
return DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp, &tm->tm_isdst);
1639
/* DetermineTimeZoneAbbrevOffsetTS()
1641
* As above but the probe time is specified as a TimestampTz (hence, UTC time),
1642
* and DST status is returned into *isdst rather than into tm->tm_isdst.
1645
DetermineTimeZoneAbbrevOffsetTS(TimestampTz ts, const char *abbr,
1646
pg_tz *tzp, int *isdst)
1648
pg_time_t t = timestamptz_to_time_t(ts);
1650
return DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp, isdst);
1654
/* DetermineTimeZoneAbbrevOffsetInternal()
1656
* Workhorse for above two functions: work from a pg_time_t probe instant.
1657
* DST status is returned into *isdst.
1660
DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr,
1661
pg_tz *tzp, int *isdst)
1663
char upabbr[TZ_STRLEN_MAX + 1];
1667
/* We need to force the abbrev to upper case */
1668
strlcpy(upabbr, abbr, sizeof(upabbr));
1669
for (p = (unsigned char *) upabbr; *p; p++)
1670
*p = pg_toupper(*p);
1672
/* Look up the abbrev's meaning at this time in this zone */
1673
if (!pg_interpret_timezone_abbrev(upabbr,
1679
(errcode(ERRCODE_CONFIG_FILE_ERROR),
1680
errmsg("time zone abbreviation \"%s\" is not used in time zone \"%s\"",
1681
abbr, pg_get_timezone_name(tzp))));
1683
/* Change sign to agree with DetermineTimeZoneOffset() */
1684
return (int) -gmtoff;
1561
1688
/* DecodeTimeOnly()
1562
1689
* Interpret parsed string as time fields only.
1563
1690
* Returns 0 if successful, DTERR code if bogus input detected.
2126
/* timezone not specified? then find local timezone if possible */
2267
* Likewise, if we had a dynamic timezone abbreviation, resolve it now.
2269
if (abbrevTz != NULL)
2275
* daylight savings time modifier but no standard timezone? then error
2277
if (fmask & DTK_M(DTZMOD))
2278
return DTERR_BAD_FORMAT;
2280
if ((fmask & DTK_DATE_M) == 0)
2281
GetCurrentDateTime(tmp);
2284
tmp->tm_year = tm->tm_year;
2285
tmp->tm_mon = tm->tm_mon;
2286
tmp->tm_mday = tm->tm_mday;
2288
tmp->tm_hour = tm->tm_hour;
2289
tmp->tm_min = tm->tm_min;
2290
tmp->tm_sec = tm->tm_sec;
2291
*tzp = DetermineTimeZoneAbbrevOffset(tmp, abbrev, abbrevTz);
2292
tm->tm_isdst = tmp->tm_isdst;
2295
/* timezone not specified? then use session timezone */
2127
2296
if (tzp != NULL && !(fmask & DTK_M(TZ)))
2129
2298
struct pg_tm tt,
2947
/* DecodeTimezoneAbbrev()
2948
* Interpret string as a timezone abbreviation, if possible.
2950
* Returns an abbreviation type (TZ, DTZ, or DYNTZ), or UNKNOWN_FIELD if
2951
* string is not any known abbreviation. On success, set *offset and *tz to
2952
* represent the UTC offset (for TZ or DTZ) or underlying zone (for DYNTZ).
2953
* Note that full timezone names (such as America/New_York) are not handled
2954
* here, mostly for historical reasons.
2956
* Given string must be lowercased already.
2958
* Implement a cache lookup since it is likely that dates
2959
* will be related in format.
2962
DecodeTimezoneAbbrev(int field, char *lowtoken,
2963
int *offset, pg_tz **tz)
2968
tp = abbrevcache[field];
2969
/* use strncmp so that we match truncated tokens */
2970
if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
2973
tp = datebsearch(lowtoken, zoneabbrevtbl->abbrevs,
2974
zoneabbrevtbl->numabbrevs);
2980
type = UNKNOWN_FIELD;
2986
abbrevcache[field] = tp;
2991
*tz = FetchDynamicTimeZone(zoneabbrevtbl, tp);
2995
*offset = tp->value;
2779
3004
/* DecodeSpecial()
2780
3005
* Decode text string using lookup table.
3007
* Recognizes the keywords listed in datetktbl.
3008
* Note: at one time this would also recognize timezone abbreviations,
3009
* but no more; use DecodeTimezoneAbbrev for that.
3011
* Given string must be lowercased already.
2782
3013
* Implement a cache lookup since it is likely that dates
2783
3014
* will be related in format.
2785
* NB: this must *not* ereport on failure;
2786
* see commands/variable.c.
2789
3017
DecodeSpecial(int field, char *lowtoken, int *val)
4142
4363
bool ok = true;
4145
for (i = 1; i < nel; i++)
4366
for (i = 0; i < nel; i++)
4147
if (strncmp(base[i - 1].token, base[i].token, TOKMAXLEN) >= 0)
4368
/* check for token strings that don't fit */
4369
if (strlen(base[i].token) > TOKMAXLEN)
4149
4371
/* %.*s is safe since all our tokens are ASCII */
4150
elog(LOG, "ordering error in %s table: \"%.*s\" >= \"%.*s\"",
4152
TOKMAXLEN, base[i - 1].token,
4153
TOKMAXLEN, base[i].token);
4372
elog(LOG, "token too long in %s table: \"%.*s\"",
4374
TOKMAXLEN + 1, base[i].token);
4376
break; /* don't risk applying strcmp */
4378
/* check for out of order */
4380
strcmp(base[i - 1].token, base[i].token) >= 0)
4382
elog(LOG, "ordering error in %s table: \"%s\" >= \"%s\"",
4209
4441
* This function gets called during timezone config file load or reload
4210
4442
* to create the final array of timezone tokens. The argument array
4211
* is already sorted in name order. The data is converted to datetkn
4212
* format and installed in *tbl, which must be allocated by the caller.
4443
* is already sorted in name order.
4445
* The result is a TimeZoneAbbrevTable (which must be a single malloc'd chunk)
4446
* or NULL on malloc failure. No other error conditions are defined.
4215
ConvertTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl,
4216
struct tzEntry *abbrevs, int n)
4448
TimeZoneAbbrevTable *
4449
ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
4218
datetkn *newtbl = tbl->abbrevs;
4451
TimeZoneAbbrevTable *tbl;
4455
/* Space for fixed fields and datetkn array */
4456
tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
4457
n * sizeof(datetkn);
4458
tbl_size = MAXALIGN(tbl_size);
4459
/* Count up space for dynamic abbreviations */
4460
for (i = 0; i < n; i++)
4462
struct tzEntry *abbr = abbrevs + i;
4464
if (abbr->zone != NULL)
4468
dsize = offsetof(DynamicZoneAbbrev, zone) +
4469
strlen(abbr->zone) + 1;
4470
tbl_size += MAXALIGN(dsize);
4474
/* Alloc the result ... */
4475
tbl = malloc(tbl_size);
4479
/* ... and fill it in */
4480
tbl->tblsize = tbl_size;
4221
4481
tbl->numabbrevs = n;
4482
/* in this loop, tbl_size reprises the space calculation above */
4483
tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
4484
n * sizeof(datetkn);
4485
tbl_size = MAXALIGN(tbl_size);
4222
4486
for (i = 0; i < n; i++)
4224
/* do NOT use strlcpy here; token field need not be null-terminated */
4225
strncpy(newtbl[i].token, abbrevs[i].abbrev, TOKMAXLEN);
4226
newtbl[i].type = abbrevs[i].is_dst ? DTZ : TZ;
4227
TOVAL(&newtbl[i], abbrevs[i].offset / MINS_PER_HOUR);
4488
struct tzEntry *abbr = abbrevs + i;
4489
datetkn *dtoken = tbl->abbrevs + i;
4491
/* use strlcpy to truncate name if necessary */
4492
strlcpy(dtoken->token, abbr->abbrev, TOKMAXLEN + 1);
4493
if (abbr->zone != NULL)
4495
/* Allocate a DynamicZoneAbbrev for this abbreviation */
4496
DynamicZoneAbbrev *dtza;
4499
dtza = (DynamicZoneAbbrev *) ((char *) tbl + tbl_size);
4501
strcpy(dtza->zone, abbr->zone);
4503
dtoken->type = DYNTZ;
4504
/* value is offset from table start to DynamicZoneAbbrev */
4505
dtoken->value = (int32) tbl_size;
4507
dsize = offsetof(DynamicZoneAbbrev, zone) +
4508
strlen(abbr->zone) + 1;
4509
tbl_size += MAXALIGN(dsize);
4513
dtoken->type = abbr->is_dst ? DTZ : TZ;
4514
dtoken->value = abbr->offset;
4518
/* Assert the two loops above agreed on size calculations */
4519
Assert(tbl->tblsize == tbl_size);
4230
4521
/* Check the ordering, if testing */
4231
Assert(CheckDateTokenTable("timezone offset", newtbl, n));
4522
Assert(CheckDateTokenTable("timezone abbreviations", tbl->abbrevs, n));
4240
4533
InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl)
4244
timezonetktbl = tbl->abbrevs;
4245
sztimezonetktbl = tbl->numabbrevs;
4247
/* clear date cache in case it contains any stale timezone names */
4248
for (i = 0; i < MAXDATEFIELDS; i++)
4249
datecache[i] = NULL;
4535
zoneabbrevtbl = tbl;
4536
/* reset abbrevcache, which may contain pointers into old table */
4537
memset(abbrevcache, 0, sizeof(abbrevcache));
4541
* Helper subroutine to locate pg_tz timezone for a dynamic abbreviation.
4544
FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp)
4546
DynamicZoneAbbrev *dtza;
4548
/* Just some sanity checks to prevent indexing off into nowhere */
4549
Assert(tp->type == DYNTZ);
4550
Assert(tp->value > 0 && tp->value < tbl->tblsize);
4552
dtza = (DynamicZoneAbbrev *) ((char *) tbl + tp->value);
4554
/* Look up the underlying zone if we haven't already */
4555
if (dtza->tz == NULL)
4557
dtza->tz = pg_tzset(dtza->zone);
4560
* Ideally we'd let the caller ereport instead of doing it here, but
4561
* then there is no way to report the bad time zone name.
4563
if (dtza->tz == NULL)
4565
(errcode(ERRCODE_CONFIG_FILE_ERROR),
4566
errmsg("time zone \"%s\" not recognized",
4568
errdetail("This time zone name appears in the configuration file for time zone abbreviation \"%s\".",
4253
4576
* This set-returning function reads all the available time zone abbreviations
4306
4632
funcctx = SRF_PERCALL_SETUP();
4307
4633
pindex = (int *) funcctx->user_fctx;
4309
if (*pindex >= sztimezonetktbl)
4635
if (zoneabbrevtbl == NULL ||
4636
*pindex >= zoneabbrevtbl->numabbrevs)
4310
4637
SRF_RETURN_DONE(funcctx);
4639
tp = zoneabbrevtbl->abbrevs + *pindex;
4644
gmtoffset = tp->value;
4648
gmtoffset = tp->value;
4653
/* Determine the current meaning of the abbrev */
4658
tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp);
4659
now = GetCurrentTransactionStartTimestamp();
4660
gmtoffset = -DetermineTimeZoneAbbrevOffsetTS(now,
4664
is_dst = (bool) isdst;
4668
elog(ERROR, "unrecognized timezone type %d", (int) tp->type);
4669
gmtoffset = 0; /* keep compiler quiet */
4312
4674
MemSet(nulls, 0, sizeof(nulls));
4315
4677
* Convert name to text, using upcasing conversion that is the inverse of
4316
4678
* what ParseDateTime() uses.
4318
strncpy(buffer, timezonetktbl[*pindex].token, TOKMAXLEN);
4319
buffer[TOKMAXLEN] = '\0'; /* may not be null-terminated */
4680
strlcpy(buffer, tp->token, sizeof(buffer));
4320
4681
for (p = (unsigned char *) buffer; *p; p++)
4321
4682
*p = pg_toupper(*p);
4323
4684
values[0] = CStringGetTextDatum(buffer);
4686
/* Convert offset (in seconds) to an interval */
4325
4687
MemSet(&tm, 0, sizeof(struct pg_tm));
4326
tm.tm_min = (-1) * FROMVAL(&timezonetktbl[*pindex]);
4688
tm.tm_sec = gmtoffset;
4327
4689
resInterval = (Interval *) palloc(sizeof(Interval));
4328
4690
tm2interval(&tm, 0, resInterval);
4329
4691
values[1] = IntervalPGetDatum(resInterval);
4331
Assert(timezonetktbl[*pindex].type == DTZ ||
4332
timezonetktbl[*pindex].type == TZ);
4333
values[2] = BoolGetDatum(timezonetktbl[*pindex].type == DTZ);
4693
values[2] = BoolGetDatum(is_dst);