1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-28 05:21:27 +03:00

Sync our copy of the timezone library with IANA release tzcode2020a.

This absorbs a leap-second-related bug fix in localtime.c, and
teaches zic to handle an expiration marker in the leapseconds file.
Neither are of any interest to us (for the foreseeable future
anyway), but we need to stay more or less in sync with upstream.

Also adjust some over-eager changes in the README from commit 957338418.
I have no intention of making changes that require C99 in this code,
until such time as all the live back branches require C99.  Otherwise
back-patching will get too exciting.

For the same reason, absorb assorted whitespace and other cosmetic
changes from HEAD into the back branches; mostly this reflects use of
improved versions of pgindent.

All in all then, quite a boring update.  But I figured I'd get it
done while I was looking at this code.
This commit is contained in:
Tom Lane 2020-06-17 18:29:29 -04:00
parent b295f666b7
commit e1c86ab97c
6 changed files with 154 additions and 88 deletions

View File

@ -55,7 +55,7 @@ match properly on the old version.
Time Zone code Time Zone code
============== ==============
The code in this directory is currently synced with tzcode release 2019b. The code in this directory is currently synced with tzcode release 2020a.
There are many cosmetic (and not so cosmetic) differences from the There are many cosmetic (and not so cosmetic) differences from the
original tzcode library, but diffs in the upstream version should usually original tzcode library, but diffs in the upstream version should usually
be propagated to our version. Here are some notes about that. be propagated to our version. Here are some notes about that.

View File

@ -36,7 +36,7 @@
* in which Daylight Saving Time is never observed. * in which Daylight Saving Time is never observed.
* 4. They might reference tzname[0] after setting to a time zone * 4. They might reference tzname[0] after setting to a time zone
* in which Standard Time is never observed. * in which Standard Time is never observed.
* 5. They might reference tm.TM_ZONE after calling offtime. * 5. They might reference tm.tm_zone after calling offtime.
* What's best to do in the above cases is open to debate; * What's best to do in the above cases is open to debate;
* for now, we just set things up so that in any of the five cases * for now, we just set things up so that in any of the five cases
* WILDABBR is used. Another possibility: initialize tzname[0] to the * WILDABBR is used. Another possibility: initialize tzname[0] to the
@ -92,8 +92,9 @@ struct rule
static struct pg_tm *gmtsub(pg_time_t const *, int32, struct pg_tm *); static struct pg_tm *gmtsub(pg_time_t const *, int32, struct pg_tm *);
static bool increment_overflow(int *, int); static bool increment_overflow(int *, int);
static bool increment_overflow_time(pg_time_t *, int32); static bool increment_overflow_time(pg_time_t *, int32);
static int64 leapcorr(struct state const *, pg_time_t);
static struct pg_tm *timesub(pg_time_t const *, int32, struct state const *, static struct pg_tm *timesub(pg_time_t const *, int32, struct state const *,
struct pg_tm *); struct pg_tm *);
static bool typesequiv(struct state const *, int, int); static bool typesequiv(struct state const *, int, int);
@ -138,7 +139,7 @@ detzcode(const char *const codep)
* Do two's-complement negation even on non-two's-complement machines. * Do two's-complement negation even on non-two's-complement machines.
* If the result would be minval - 1, return minval. * If the result would be minval - 1, return minval.
*/ */
result -= !TWOS_COMPLEMENT(int32) &&result != 0; result -= !TWOS_COMPLEMENT(int32) && result != 0;
result += minval; result += minval;
} }
return result; return result;
@ -152,7 +153,7 @@ detzcode64(const char *const codep)
int64 one = 1; int64 one = 1;
int64 halfmaxval = one << (64 - 2); int64 halfmaxval = one << (64 - 2);
int64 maxval = halfmaxval - 1 + halfmaxval; int64 maxval = halfmaxval - 1 + halfmaxval;
int64 minval = -TWOS_COMPLEMENT(int64) -maxval; int64 minval = -TWOS_COMPLEMENT(int64) - maxval;
result = codep[0] & 0x7f; result = codep[0] & 0x7f;
for (i = 1; i < 8; ++i) for (i = 1; i < 8; ++i)
@ -164,7 +165,7 @@ detzcode64(const char *const codep)
* Do two's-complement negation even on non-two's-complement machines. * Do two's-complement negation even on non-two's-complement machines.
* If the result would be minval - 1, return minval. * If the result would be minval - 1, return minval.
*/ */
result -= !TWOS_COMPLEMENT(int64) &&result != 0; result -= !TWOS_COMPLEMENT(int64) && result != 0;
result += minval; result += minval;
} }
return result; return result;
@ -173,7 +174,7 @@ detzcode64(const char *const codep)
static bool static bool
differ_by_repeat(const pg_time_t t1, const pg_time_t t0) differ_by_repeat(const pg_time_t t1, const pg_time_t t0)
{ {
if (TYPE_BIT(pg_time_t) -TYPE_SIGNED(pg_time_t) <SECSPERREPEAT_BITS) if (TYPE_BIT(pg_time_t) - TYPE_SIGNED(pg_time_t) < SECSPERREPEAT_BITS)
return 0; return 0;
return t1 - t0 == SECSPERREPEAT; return t1 - t0 == SECSPERREPEAT;
} }
@ -477,12 +478,14 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
for (i = 0; i < ts->timecnt; i++) for (i = 0; i < ts->timecnt; i++)
if (sp->timecnt == 0 if (sp->timecnt == 0
|| sp->ats[sp->timecnt - 1] < ts->ats[i]) || (sp->ats[sp->timecnt - 1]
< ts->ats[i] + leapcorr(sp, ts->ats[i])))
break; break;
while (i < ts->timecnt while (i < ts->timecnt
&& sp->timecnt < TZ_MAX_TIMES) && sp->timecnt < TZ_MAX_TIMES)
{ {
sp->ats[sp->timecnt] = ts->ats[i]; sp->ats[sp->timecnt]
= ts->ats[i] + leapcorr(sp, ts->ats[i]);
sp->types[sp->timecnt] = (sp->typecnt sp->types[sp->timecnt] = (sp->typecnt
+ ts->types[i]); + ts->types[i]);
sp->timecnt++; sp->timecnt++;
@ -1480,7 +1483,7 @@ timesub(const pg_time_t *timep, int32 offset,
int leapdays; int leapdays;
tdelta = tdays / DAYSPERLYEAR; tdelta = tdays / DAYSPERLYEAR;
if (!((!TYPE_SIGNED(pg_time_t) ||INT_MIN <= tdelta) if (!((!TYPE_SIGNED(pg_time_t) || INT_MIN <= tdelta)
&& tdelta <= INT_MAX)) && tdelta <= INT_MAX))
goto out_of_range; goto out_of_range;
idelta = tdelta; idelta = tdelta;
@ -1601,6 +1604,22 @@ increment_overflow_time(pg_time_t *tp, int32 j)
return false; return false;
} }
static int64
leapcorr(struct state const *sp, pg_time_t t)
{
struct lsinfo const *lp;
int i;
i = sp->leapcnt;
while (--i >= 0)
{
lp = &sp->lsis[i];
if (t >= lp->ls_trans)
return lp->ls_corr;
}
return 0;
}
/* /*
* Find the next DST transition time in the given zone after the given time * Find the next DST transition time in the given zone after the given time
* *

View File

@ -32,8 +32,8 @@ pg_tz *log_timezone = NULL;
static bool scan_directory_ci(const char *dirname, static bool scan_directory_ci(const char *dirname,
const char *fname, int fnamelen, const char *fname, int fnamelen,
char *canonname, int canonnamelen); char *canonname, int canonnamelen);
/* /*
@ -364,7 +364,7 @@ pg_tzset_offset(long gmtoffset)
* is to ensure that log_timezone has a valid value before any logging GUC * is to ensure that log_timezone has a valid value before any logging GUC
* variables could become set to values that require elog.c to provide * variables could become set to values that require elog.c to provide
* timestamps (e.g., log_line_prefix). We may as well initialize * timestamps (e.g., log_line_prefix). We may as well initialize
* session_timestamp to something valid, too. * session_timezone to something valid, too.
*/ */
void void
pg_timezone_initialize(void) pg_timezone_initialize(void)

View File

@ -74,8 +74,8 @@ struct pg_tz
extern int pg_open_tzfile(const char *name, char *canonname); extern int pg_open_tzfile(const char *name, char *canonname);
/* in localtime.c */ /* in localtime.c */
extern int tzload(const char *name, char *canonname, struct state *sp, extern int tzload(const char *name, char *canonname, struct state *sp,
bool doextend); bool doextend);
extern bool tzparse(const char *name, struct state *sp, bool lastditch); extern bool tzparse(const char *name, struct state *sp, bool lastditch);
#endif /* _PGTZ_H */ #endif /* _PGTZ_H */

View File

@ -114,10 +114,16 @@ enum warn
static char *_add(const char *, char *, const char *); static char *_add(const char *, char *, const char *);
static char *_conv(int, const char *, char *, const char *); static char *_conv(int, const char *, char *, const char *);
static char *_fmt(const char *, const struct pg_tm *, char *, const char *, static char *_fmt(const char *, const struct pg_tm *, char *, const char *,
enum warn *); enum warn *);
static char *_yconv(int, int, bool, bool, char *, char const *); static char *_yconv(int, int, bool, bool, char *, char const *);
/*
* Convert timestamp t to string s, a caller-allocated buffer of size maxsize,
* using the given format pattern.
*
* See also timestamptz_to_str.
*/
size_t size_t
pg_strftime(char *s, size_t maxsize, const char *format, const struct pg_tm *t) pg_strftime(char *s, size_t maxsize, const char *format, const struct pg_tm *t)
{ {
@ -499,7 +505,7 @@ _fmt(const char *format, const struct pg_tm *t, char *pt,
static char * static char *
_conv(int n, const char *format, char *pt, const char *ptlim) _conv(int n, const char *format, char *pt, const char *ptlim)
{ {
char buf[INT_STRLEN_MAXIMUM(int) +1]; char buf[INT_STRLEN_MAXIMUM(int) + 1];
sprintf(buf, format, n); sprintf(buf, format, n);
return _add(buf, pt, ptlim); return _add(buf, pt, ptlim);

View File

@ -125,13 +125,14 @@ static void warning(const char *string,...) pg_attribute_printf(1, 2);
static void usage(FILE *stream, int status) pg_attribute_noreturn(); static void usage(FILE *stream, int status) pg_attribute_noreturn();
static void addtt(zic_t starttime, int type); static void addtt(zic_t starttime, int type);
static int addtype(zic_t, char const *, bool, bool, bool); static int addtype(zic_t, char const *, bool, bool, bool);
static void leapadd(zic_t, bool, int, int); static void leapadd(zic_t, int, int);
static void adjleap(void); static void adjleap(void);
static void associate(void); static void associate(void);
static void dolink(const char *, const char *, bool); static void dolink(const char *, const char *, bool);
static char **getfields(char *buf); static char **getfields(char *buf);
static zic_t gethms(const char *string, const char *errstring); static zic_t gethms(const char *string, const char *errstring);
static zic_t getsave(char *, bool *); static zic_t getsave(char *, bool *);
static void inexpires(char **, int);
static void infile(const char *filename); static void infile(const char *filename);
static void inleap(char **fields, int nfields); static void inleap(char **fields, int nfields);
static void inlink(char **fields, int nfields); static void inlink(char **fields, int nfields);
@ -149,9 +150,9 @@ static zic_t oadd(zic_t t1, zic_t t2);
static void outzone(const struct zone *zp, ptrdiff_t ntzones); static void outzone(const struct zone *zp, ptrdiff_t ntzones);
static zic_t rpytime(const struct rule *rp, zic_t wantedy); static zic_t rpytime(const struct rule *rp, zic_t wantedy);
static void rulesub(struct rule *rp, static void rulesub(struct rule *rp,
const char *loyearp, const char *hiyearp, const char *loyearp, const char *hiyearp,
const char *typep, const char *monthp, const char *typep, const char *monthp,
const char *dayp, const char *timep); const char *dayp, const char *timep);
static zic_t tadd(zic_t t1, zic_t t2); static zic_t tadd(zic_t t1, zic_t t2);
static bool yearistype(zic_t year, const char *type); static bool yearistype(zic_t year, const char *type);
@ -202,6 +203,7 @@ static int typecnt;
#define LC_ZONE 1 #define LC_ZONE 1
#define LC_LINK 2 #define LC_LINK 2
#define LC_LEAP 3 #define LC_LEAP 3
#define LC_EXPIRES 4
/* /*
* Which fields are which on a Zone line. * Which fields are which on a Zone line.
@ -267,6 +269,9 @@ static int typecnt;
#define LP_ROLL 6 #define LP_ROLL 6
#define LEAP_FIELDS 7 #define LEAP_FIELDS 7
/* Expires lines are like Leap lines, except without CORR and ROLL fields. */
#define EXPIRES_FIELDS 5
/* /*
* Year synonyms. * Year synonyms.
*/ */
@ -302,7 +307,7 @@ struct lookup
}; };
static struct lookup const *byword(const char *string, static struct lookup const *byword(const char *string,
const struct lookup *lp); const struct lookup *lp);
static struct lookup const zi_line_codes[] = { static struct lookup const zi_line_codes[] = {
{"Rule", LC_RULE}, {"Rule", LC_RULE},
@ -312,6 +317,7 @@ static struct lookup const zi_line_codes[] = {
}; };
static struct lookup const leap_line_codes[] = { static struct lookup const leap_line_codes[] = {
{"Leap", LC_LEAP}, {"Leap", LC_LEAP},
{"Expires", LC_EXPIRES},
{NULL, 0} {NULL, 0}
}; };
@ -584,6 +590,12 @@ static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE); static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
/* The time specified by an Expires line, or negative if no such line. */
static zic_t leapexpires = -1;
/* The time specified by an #expires comment, or negative if no such line. */
static zic_t comment_leapexpires = -1;
/* Set the time range of the output to TIMERANGE. /* Set the time range of the output to TIMERANGE.
Return true if successful. */ Return true if successful. */
static bool static bool
@ -652,7 +664,7 @@ main(int argc, char **argv)
umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
#endif #endif
progname = argv[0]; progname = argv[0];
if (TYPE_BIT(zic_t) <64) if (TYPE_BIT(zic_t) < 64)
{ {
fprintf(stderr, "%s: %s\n", progname, fprintf(stderr, "%s: %s\n", progname,
_("wild compilation-time specification of zic_t")); _("wild compilation-time specification of zic_t"));
@ -1279,7 +1291,8 @@ infile(const char *name)
} }
if (nfields == 0) if (nfields == 0)
{ {
/* nothing to do */ if (name == leapsec && *buf == '#')
sscanf(buf, "#expires " INT64_FORMAT, &comment_leapexpires);
} }
else if (wantcont) else if (wantcont)
{ {
@ -1311,6 +1324,10 @@ infile(const char *name)
inleap(fields, nfields); inleap(fields, nfields);
wantcont = false; wantcont = false;
break; break;
case LC_EXPIRES:
inexpires(fields, nfields);
wantcont = false;
break;
default: /* "cannot happen" */ default: /* "cannot happen" */
fprintf(stderr, fprintf(stderr,
_("%s: panic: Invalid l_value %d\n"), _("%s: panic: Invalid l_value %d\n"),
@ -1634,8 +1651,8 @@ inzsub(char **fields, int nfields, bool iscont)
return hasuntil; return hasuntil;
} }
static void static zic_t
inleap(char **fields, int nfields) getleapdatetime(char **fields, int nfields, bool expire_line)
{ {
const char *cp; const char *cp;
const struct lookup *lp; const struct lookup *lp;
@ -1651,11 +1668,6 @@ inleap(char **fields, int nfields)
zic_t t; zic_t t;
char xs; char xs;
if (nfields != LEAP_FIELDS)
{
error(_("wrong number of fields on Leap line"));
return;
}
dayoff = 0; dayoff = 0;
cp = fields[LP_YEAR]; cp = fields[LP_YEAR];
if (sscanf(cp, "%d%c", &year, &xs) != 1) if (sscanf(cp, "%d%c", &year, &xs) != 1)
@ -1664,13 +1676,16 @@ inleap(char **fields, int nfields)
* Leapin' Lizards! * Leapin' Lizards!
*/ */
error(_("invalid leaping year")); error(_("invalid leaping year"));
return; return -1;
}
if (!expire_line)
{
if (!leapseen || leapmaxyear < year)
leapmaxyear = year;
if (!leapseen || leapminyear > year)
leapminyear = year;
leapseen = true;
} }
if (!leapseen || leapmaxyear < year)
leapmaxyear = year;
if (!leapseen || leapminyear > year)
leapminyear = year;
leapseen = true;
j = EPOCH_YEAR; j = EPOCH_YEAR;
while (j != year) while (j != year)
{ {
@ -1689,7 +1704,7 @@ inleap(char **fields, int nfields)
if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL)
{ {
error(_("invalid month name")); error(_("invalid month name"));
return; return -1;
} }
month = lp->l_value; month = lp->l_value;
j = TM_JANUARY; j = TM_JANUARY;
@ -1704,56 +1719,70 @@ inleap(char **fields, int nfields)
day <= 0 || day > len_months[isleap(year)][month]) day <= 0 || day > len_months[isleap(year)][month])
{ {
error(_("invalid day of month")); error(_("invalid day of month"));
return; return -1;
} }
dayoff = oadd(dayoff, day - 1); dayoff = oadd(dayoff, day - 1);
if (dayoff < min_time / SECSPERDAY) if (dayoff < min_time / SECSPERDAY)
{ {
error(_("time too small")); error(_("time too small"));
return; return -1;
} }
if (dayoff > max_time / SECSPERDAY) if (dayoff > max_time / SECSPERDAY)
{ {
error(_("time too large")); error(_("time too large"));
return; return -1;
} }
t = dayoff * SECSPERDAY; t = dayoff * SECSPERDAY;
tod = gethms(fields[LP_TIME], _("invalid time of day")); tod = gethms(fields[LP_TIME], _("invalid time of day"));
cp = fields[LP_CORR]; t = tadd(t, tod);
{ if (t < 0)
bool positive; error(_("leap second precedes Epoch"));
int count; return t;
}
if (strcmp(cp, "") == 0) static void
{ /* infile() turns "-" into "" */ inleap(char **fields, int nfields)
positive = false; {
count = 1; if (nfields != LEAP_FIELDS)
} error(_("wrong number of fields on Leap line"));
else if (strcmp(cp, "+") == 0) else
{
zic_t t = getleapdatetime(fields, nfields, false);
if (0 <= t)
{ {
positive = true; struct lookup const *lp = byword(fields[LP_ROLL], leap_types);
count = 1;
if (!lp)
error(_("invalid Rolling/Stationary field on Leap line"));
else
{
int correction = 0;
if (!fields[LP_CORR][0]) /* infile() turns "-" into "". */
correction = -1;
else if (strcmp(fields[LP_CORR], "+") == 0)
correction = 1;
else
error(_("invalid CORRECTION field on Leap line"));
if (correction)
leapadd(t, correction, lp->l_value);
}
} }
else
{
error(_("illegal CORRECTION field on Leap line"));
return;
}
if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL)
{
error(_("illegal Rolling/Stationary field on Leap line"));
return;
}
t = tadd(t, tod);
if (t < 0)
{
error(_("leap second precedes Epoch"));
return;
}
leapadd(t, positive, lp->l_value, count);
} }
} }
static void
inexpires(char **fields, int nfields)
{
if (nfields != EXPIRES_FIELDS)
error(_("wrong number of fields on Expires line"));
else if (0 <= leapexpires)
error(_("multiple Expires lines"));
else
leapexpires = getleapdatetime(fields, nfields, true);
}
static void static void
inlink(char **fields, int nfields) inlink(char **fields, int nfields)
{ {
@ -3369,12 +3398,11 @@ addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
} }
static void static void
leapadd(zic_t t, bool positive, int rolling, int count) leapadd(zic_t t, int correction, int rolling)
{ {
int i, int i;
j;
if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) if (TZ_MAX_LEAPS <= leapcnt)
{ {
error(_("too many leap seconds")); error(_("too many leap seconds"));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -3382,19 +3410,13 @@ leapadd(zic_t t, bool positive, int rolling, int count)
for (i = 0; i < leapcnt; ++i) for (i = 0; i < leapcnt; ++i)
if (t <= trans[i]) if (t <= trans[i])
break; break;
do memmove(&trans[i + 1], &trans[i], (leapcnt - i) * sizeof *trans);
{ memmove(&corr[i + 1], &corr[i], (leapcnt - i) * sizeof *corr);
for (j = leapcnt; j > i; --j) memmove(&roll[i + 1], &roll[i], (leapcnt - i) * sizeof *roll);
{ trans[i] = t;
trans[j] = trans[j - 1]; corr[i] = correction;
corr[j] = corr[j - 1]; roll[i] = rolling;
roll[j] = roll[j - 1]; ++leapcnt;
}
trans[i] = t;
corr[i] = positive ? 1 : -count;
roll[i] = rolling;
++leapcnt;
} while (positive && --count != 0);
} }
static void static void
@ -3418,6 +3440,25 @@ adjleap(void)
trans[i] = tadd(trans[i], last); trans[i] = tadd(trans[i], last);
last = corr[i] += last; last = corr[i] += last;
} }
if (leapexpires < 0)
{
leapexpires = comment_leapexpires;
if (0 <= leapexpires)
warning(_("\"#expires\" is obsolescent; use \"Expires\""));
}
if (0 <= leapexpires)
{
leapexpires = oadd(leapexpires, last);
if (!(leapcnt == 0 || (trans[leapcnt - 1] < leapexpires)))
{
error(_("last Leap time does not precede Expires time"));
exit(EXIT_FAILURE);
}
if (leapexpires <= hi_time)
hi_time = leapexpires - 1;
}
} }
static char * static char *
@ -3444,7 +3485,7 @@ yearistype(zic_t year, const char *type)
if (type == NULL || *type == '\0') if (type == NULL || *type == '\0')
return true; return true;
buf = emalloc(1 + 4 * strlen(yitcommand) + 2 buf = emalloc(1 + 4 * strlen(yitcommand) + 2
+ INT_STRLEN_MAXIMUM(zic_t) +2 + 4 * strlen(type) + 2); + INT_STRLEN_MAXIMUM(zic_t) + 2 + 4 * strlen(type) + 2);
b = shellquote(buf, yitcommand); b = shellquote(buf, yitcommand);
*b++ = ' '; *b++ = ' ';
b += sprintf(b, INT64_FORMAT, year); b += sprintf(b, INT64_FORMAT, year);