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 6924c37f77
commit d8b15eeb8a
3 changed files with 143 additions and 69 deletions

View File

@ -55,7 +55,7 @@ match properly on the old version.
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
original tzcode library, but diffs in the upstream version should usually
be propagated to our version. Here are some notes about that.
@ -71,7 +71,14 @@ fixed that.)
* We need the code to follow Postgres' portability conventions; this
includes relying on configure's results rather than hand-hacked
#defines (see private.h).
#defines (see private.h in particular).
* Similarly, avoid relying on <stdint.h> features that may not exist on old
systems. In particular this means using Postgres' definitions of the int32
and int64 typedefs, not int_fast32_t/int_fast64_t. Likewise we use
PG_INT32_MIN/MAX not INT32_MIN/MAX. (Once we desupport all PG versions
that don't require C99, it'd be practical to rely on <stdint.h> and remove
this set of diffs; but that day is not yet.)
* Since Postgres is typically built on a system that has its own copy
of the <time.h> functions, we must avoid conflicting with those. This
@ -109,6 +116,13 @@ to first run the tzcode source files through a sed filter like this:
-e 's|^\*/| */|' \
-e 's/\bregister[ \t]//g' \
-e 's/\bATTRIBUTE_PURE[ \t]//g' \
-e 's/int_fast32_t/int32/g' \
-e 's/int_fast64_t/int64/g' \
-e 's/intmax_t/int64/g' \
-e 's/INT32_MIN/PG_INT32_MIN/g' \
-e 's/INT32_MAX/PG_INT32_MAX/g' \
-e 's/INTMAX_MIN/PG_INT64_MIN/g' \
-e 's/INTMAX_MAX/PG_INT64_MAX/g' \
-e 's/struct[ \t]+tm\b/struct pg_tm/g' \
-e 's/\btime_t\b/pg_time_t/g' \
-e 's/lineno/lineno_t/g' \

View File

@ -92,6 +92,7 @@ struct rule
static struct pg_tm *gmtsub(pg_time_t const *, int32, struct pg_tm *);
static bool increment_overflow(int *, int);
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 *,
struct pg_tm *);
static bool typesequiv(struct state const *, int, int);
@ -477,12 +478,14 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
for (i = 0; i < ts->timecnt; i++)
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;
while (i < ts->timecnt
&& 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
+ ts->types[i]);
sp->timecnt++;
@ -1601,6 +1604,22 @@ increment_overflow_time(pg_time_t *tp, int32 j)
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
*

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 addtt(zic_t starttime, int type);
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 associate(void);
static void dolink(const char *, const char *, bool);
static char **getfields(char *buf);
static zic_t gethms(const char *string, const char *errstring);
static zic_t getsave(char *, bool *);
static void inexpires(char **, int);
static void infile(const char *filename);
static void inleap(char **fields, int nfields);
static void inlink(char **fields, int nfields);
@ -202,6 +203,7 @@ static int typecnt;
#define LC_ZONE 1
#define LC_LINK 2
#define LC_LEAP 3
#define LC_EXPIRES 4
/*
* Which fields are which on a Zone line.
@ -267,6 +269,9 @@ static int typecnt;
#define LP_ROLL 6
#define LEAP_FIELDS 7
/* Expires lines are like Leap lines, except without CORR and ROLL fields. */
#define EXPIRES_FIELDS 5
/*
* Year synonyms.
*/
@ -312,6 +317,7 @@ static struct lookup const zi_line_codes[] = {
};
static struct lookup const leap_line_codes[] = {
{"Leap", LC_LEAP},
{"Expires", LC_EXPIRES},
{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 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.
Return true if successful. */
static bool
@ -1279,7 +1291,8 @@ infile(const char *name)
}
if (nfields == 0)
{
/* nothing to do */
if (name == leapsec && *buf == '#')
sscanf(buf, "#expires " INT64_FORMAT, &comment_leapexpires);
}
else if (wantcont)
{
@ -1311,6 +1324,10 @@ infile(const char *name)
inleap(fields, nfields);
wantcont = false;
break;
case LC_EXPIRES:
inexpires(fields, nfields);
wantcont = false;
break;
default: /* "cannot happen" */
fprintf(stderr,
_("%s: panic: Invalid l_value %d\n"),
@ -1634,8 +1651,8 @@ inzsub(char **fields, int nfields, bool iscont)
return hasuntil;
}
static void
inleap(char **fields, int nfields)
static zic_t
getleapdatetime(char **fields, int nfields, bool expire_line)
{
const char *cp;
const struct lookup *lp;
@ -1651,11 +1668,6 @@ inleap(char **fields, int nfields)
zic_t t;
char xs;
if (nfields != LEAP_FIELDS)
{
error(_("wrong number of fields on Leap line"));
return;
}
dayoff = 0;
cp = fields[LP_YEAR];
if (sscanf(cp, "%d%c", &year, &xs) != 1)
@ -1664,13 +1676,16 @@ inleap(char **fields, int nfields)
* Leapin' Lizards!
*/
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;
while (j != year)
{
@ -1689,7 +1704,7 @@ inleap(char **fields, int nfields)
if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL)
{
error(_("invalid month name"));
return;
return -1;
}
month = lp->l_value;
j = TM_JANUARY;
@ -1704,56 +1719,70 @@ inleap(char **fields, int nfields)
day <= 0 || day > len_months[isleap(year)][month])
{
error(_("invalid day of month"));
return;
return -1;
}
dayoff = oadd(dayoff, day - 1);
if (dayoff < min_time / SECSPERDAY)
{
error(_("time too small"));
return;
return -1;
}
if (dayoff > max_time / SECSPERDAY)
{
error(_("time too large"));
return;
return -1;
}
t = dayoff * SECSPERDAY;
tod = gethms(fields[LP_TIME], _("invalid time of day"));
cp = fields[LP_CORR];
{
bool positive;
int count;
t = tadd(t, tod);
if (t < 0)
error(_("leap second precedes Epoch"));
return t;
}
if (strcmp(cp, "") == 0)
{ /* infile() turns "-" into "" */
positive = false;
count = 1;
}
else if (strcmp(cp, "+") == 0)
static void
inleap(char **fields, int nfields)
{
if (nfields != LEAP_FIELDS)
error(_("wrong number of fields on Leap line"));
else
{
zic_t t = getleapdatetime(fields, nfields, false);
if (0 <= t)
{
positive = true;
count = 1;
struct lookup const *lp = byword(fields[LP_ROLL], leap_types);
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
inlink(char **fields, int nfields)
{
@ -3369,12 +3398,11 @@ addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
}
static void
leapadd(zic_t t, bool positive, int rolling, int count)
leapadd(zic_t t, int correction, int rolling)
{
int i,
j;
int i;
if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS)
if (TZ_MAX_LEAPS <= leapcnt)
{
error(_("too many leap seconds"));
exit(EXIT_FAILURE);
@ -3382,19 +3410,13 @@ leapadd(zic_t t, bool positive, int rolling, int count)
for (i = 0; i < leapcnt; ++i)
if (t <= trans[i])
break;
do
{
for (j = leapcnt; j > i; --j)
{
trans[j] = trans[j - 1];
corr[j] = corr[j - 1];
roll[j] = roll[j - 1];
}
trans[i] = t;
corr[i] = positive ? 1 : -count;
roll[i] = rolling;
++leapcnt;
} while (positive && --count != 0);
memmove(&trans[i + 1], &trans[i], (leapcnt - i) * sizeof *trans);
memmove(&corr[i + 1], &corr[i], (leapcnt - i) * sizeof *corr);
memmove(&roll[i + 1], &roll[i], (leapcnt - i) * sizeof *roll);
trans[i] = t;
corr[i] = correction;
roll[i] = rolling;
++leapcnt;
}
static void
@ -3418,6 +3440,25 @@ adjleap(void)
trans[i] = tadd(trans[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 *