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

The non-cosmetic changes involve teaching the "zic" tzdata compiler about
negative DST.  While I'm not currently intending that we start using
negative-DST data right away, it seems possible that somebody would try
to use our copy of zic with bleeding-edge IANA data.  So we'd better be
out in front of this change code-wise, even though it doesn't matter for
the data file we're shipping.

Discussion: https://postgr.es/m/30996.1525445902@sss.pgh.pa.us
This commit is contained in:
Tom Lane 2018-05-04 12:26:25 -04:00
parent 59cb323053
commit b45f6613e0
4 changed files with 129 additions and 46 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 2017c.
The code in this directory is currently synced with tzcode release 2018e.
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.

View File

@ -60,9 +60,8 @@ static int tzdefrules_loaded = 0;
/*
* The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
* Default to US rules as of 2017-05-07.
* POSIX 1003.1 section 8.1.1 says that the default DST rules are
* implementation dependent; for historical reasons, US rules are a
* common default.
* POSIX does not specify the default DST rules;
* for historical reasons, US rules are a common default.
*/
#define TZDEFRULESTRING ",M3.2.0,M11.1.0"
@ -1158,10 +1157,11 @@ tzparse(const char *name, struct state *sp, bool lastditch)
else
{
/*
* If summer time is in effect, and the transition time
* was not specified as standard time, add the summer time
* offset to the transition time; otherwise, add the
* standard time offset to the transition time.
* If daylight saving time is in effect, and the
* transition time was not specified as standard time, add
* the daylight saving time offset to the transition time;
* otherwise, add the standard time offset to the
* transition time.
*/
/*

View File

@ -203,7 +203,7 @@ _fmt(const char *format, const struct pg_tm *t, char *pt,
/*
* Locale modifiers of C99 and later. The sequences %Ec
* %EC %Ex %EX %Ey %EY %Od %oe %OH %OI %Om %OM %OS %Ou %OU
* %OV %Ow %OW %Oy are supposed to provide alternate
* %OV %Ow %OW %Oy are supposed to provide alternative
* representations.
*/
goto label;

View File

@ -39,6 +39,10 @@ typedef int64 zic_t;
#define linkat(fromdir, from, todir, to, flag) \
(itssymlink(from) ? (errno = ENOTSUP, -1) : link(from, to))
#endif
/* Port to native MS-Windows and to ancient UNIX. */
#if !defined S_ISDIR && defined S_IFDIR && defined S_IFMT
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif
/* The maximum ptrdiff_t value, for pre-C99 platforms. */
#ifndef PTRDIFF_MAX
@ -74,7 +78,9 @@ struct rule
bool r_todisstd; /* above is standard time if 1 or wall clock
* time if 0 */
bool r_todisgmt; /* above is GMT if 1 or local time if 0 */
zic_t r_stdoff; /* offset from standard time */
bool r_isdst; /* is this daylight saving time? */
zic_t r_stdoff; /* offset from default time (which is usually
* standard time) */
const char *r_abbrvar; /* variable part of abbreviation */
bool r_todo; /* a rule to do (used in outzone) */
@ -96,10 +102,11 @@ struct zone
const char *z_name;
zic_t z_gmtoff;
const char *z_rule;
char *z_rule;
const char *z_format;
char z_format_specifier;
bool z_isdst;
zic_t z_stdoff;
struct rule *z_rules;
@ -125,6 +132,7 @@ static void dolink(const char *, const char *, bool);
static char **getfields(char *buf);
static zic_t gethms(const char *string, const char *errstring,
bool);
static zic_t getstdoff(char *, bool *);
static void infile(const char *filename);
static void inleap(char **fields, int nfields);
static void inlink(char **fields, int nfields);
@ -530,7 +538,7 @@ usage(FILE *stream, int status)
fprintf(stream,
_("%s: usage is %s [ --version ] [ --help ] [ -v ] [ -P ] \\\n"
"\t[ -l localtime ] [ -p posixrules ] [ -d directory ] \\\n"
"\t[ -L leapseconds ] [ filename ... ]\n\n"
"\t[ -t localtime-link ] [ -L leapseconds ] [ filename ... ]\n\n"
"Report bugs to %s.\n"),
progname, progname, PACKAGE_BUGREPORT);
if (status == EXIT_SUCCESS)
@ -566,6 +574,7 @@ static const char *psxrules;
static const char *lcltime;
static const char *directory;
static const char *leapsec;
static const char *tzdefault;
static const char *yitcommand;
int
@ -597,7 +606,7 @@ main(int argc, char **argv)
{
usage(stdout, EXIT_SUCCESS);
}
while ((c = getopt(argc, argv, "d:l:p:L:vPsy:")) != EOF && c != -1)
while ((c = getopt(argc, argv, "d:l:L:p:Pst:vy:")) != EOF && c != -1)
switch (c)
{
default:
@ -635,6 +644,17 @@ main(int argc, char **argv)
return EXIT_FAILURE;
}
break;
case 't':
if (tzdefault != NULL)
{
fprintf(stderr,
_("%s: More than one -t option"
" specified\n"),
progname);
return EXIT_FAILURE;
}
tzdefault = optarg;
break;
case 'y':
if (yitcommand == NULL)
{
@ -675,6 +695,8 @@ main(int argc, char **argv)
usage(stderr, EXIT_FAILURE); /* usage message by request */
if (directory == NULL)
directory = "data";
if (tzdefault == NULL)
tzdefault = TZDEFAULT;
if (yitcommand == NULL)
yitcommand = "yearistype";
@ -716,7 +738,7 @@ main(int argc, char **argv)
if (lcltime != NULL)
{
eat(_("command line"), 1);
dolink(lcltime, TZDEFAULT, true);
dolink(lcltime, tzdefault, true);
}
if (psxrules != NULL)
{
@ -916,10 +938,12 @@ dolink(char const *fromfield, char const *tofield, bool staysymlink)
char const *contents = absolute ? fromfield : linkalloc;
int symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
if (symlink_errno == ENOENT && !todirs_made)
if (!todirs_made
&& (symlink_errno == ENOENT || symlink_errno == ENOTSUP))
{
mkdirs(tofield, true);
symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
if (symlink_errno == ENOENT)
symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
}
free(linkalloc);
if (symlink_errno == 0)
@ -1140,8 +1164,7 @@ associate(void)
* Maybe we have a local standard time offset.
*/
eat(zp->z_filename, zp->z_linenum);
zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
true);
zp->z_stdoff = getstdoff(zp->z_rule, &zp->z_isdst);
/*
* Note, though, that if there's no rule, a '%s' in the format is
@ -1263,10 +1286,16 @@ gethms(char const *string, char const *errstring, bool signable)
{
/* PG: make hh be int not zic_t to avoid sscanf portability issues */
int hh;
int mm,
ss,
sign;
char xs;
int sign,
mm = 0,
ss = 0;
char hhx,
mmx,
ssx,
xr = '0',
xs;
int tenths = 0;
bool ok = true;
if (string == NULL || *string == '\0')
return 0;
@ -1279,12 +1308,32 @@ gethms(char const *string, char const *errstring, bool signable)
}
else
sign = 1;
if (sscanf(string, "%d%c", &hh, &xs) == 1)
mm = ss = 0;
else if (sscanf(string, "%d:%d%c", &hh, &mm, &xs) == 2)
ss = 0;
else if (sscanf(string, "%d:%d:%d%c", &hh, &mm, &ss, &xs)
!= 3)
switch (sscanf(string,
"%d%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
&hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs))
{
default:
ok = false;
break;
case 8:
ok = '0' <= xr && xr <= '9';
/* fallthrough */
case 7:
ok &= ssx == '.';
if (ok && noise)
warning(_("fractional seconds rejected by"
" pre-2018 versions of zic"));
/* fallthrough */
case 5:
ok &= mmx == ':';
/* fallthrough */
case 3:
ok &= hhx == ':';
/* fallthrough */
case 1:
break;
}
if (!ok)
{
error("%s", errstring);
return 0;
@ -1304,6 +1353,7 @@ gethms(char const *string, char const *errstring, bool signable)
return 0;
}
#endif
ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths; /* Round to even. */
if (noise && (hh > HOURSPERDAY ||
(hh == HOURSPERDAY && (mm != 0 || ss != 0))))
warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
@ -1311,6 +1361,34 @@ gethms(char const *string, char const *errstring, bool signable)
sign * (mm * SECSPERMIN + ss));
}
static zic_t
getstdoff(char *field, bool *isdst)
{
int dst = -1;
zic_t stdoff;
size_t fieldlen = strlen(field);
if (fieldlen != 0)
{
char *ep = field + fieldlen - 1;
switch (*ep)
{
case 'd':
dst = 1;
*ep = '\0';
break;
case 's':
dst = 0;
*ep = '\0';
break;
}
}
stdoff = gethms(field, _("invalid saved time"), true);
*isdst = dst < 0 ? stdoff != 0 : dst;
return stdoff;
}
static void
inrule(char **fields, int nfields)
{
@ -1328,7 +1406,7 @@ inrule(char **fields, int nfields)
}
r.r_filename = filename;
r.r_linenum = linenum;
r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), true);
r.r_stdoff = getstdoff(fields[RF_STDOFF], &r.r_isdst);
rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
r.r_name = ecpyalloc(fields[RF_NAME]);
@ -1349,11 +1427,11 @@ inzone(char **fields, int nfields)
error(_("wrong number of fields on Zone line"));
return false;
}
if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL)
if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0)
{
error(
_("\"Zone %s\" line and -l option are mutually exclusive"),
TZDEFAULT);
tzdefault);
return false;
}
if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL)
@ -2171,7 +2249,7 @@ writezone(const char *const name, const char *const string, char version)
}
#define DO(field) fwrite(tzh.field, sizeof tzh.field, 1, fp)
tzh = tzh0;
strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
memcpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
tzh.tzh_version[0] = version;
convert(thistypecnt, tzh.tzh_ttisgmtcnt);
convert(thistypecnt, tzh.tzh_ttisstdcnt);
@ -2299,7 +2377,7 @@ abbroffset(char *buf, zic_t offset)
offset /= MINSPERHOUR;
if (100 <= offset)
{
error(_("%%z UTC offset magnitude exceeds 99:59:59"));
error(_("%%z UT offset magnitude exceeds 99:59:59"));
return "%z";
}
else
@ -2326,7 +2404,7 @@ abbroffset(char *buf, zic_t offset)
static size_t
doabbr(char *abbr, struct zone const *zp, char const *letters,
zic_t stdoff, bool doquotes)
bool isdst, zic_t stdoff, bool doquotes)
{
char *cp;
char *slashp;
@ -2344,7 +2422,7 @@ doabbr(char *abbr, struct zone const *zp, char const *letters,
letters = "%s";
sprintf(abbr, format, letters);
}
else if (stdoff != 0)
else if (isdst)
{
strcpy(abbr, slashp + 1);
}
@ -2471,7 +2549,7 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
}
if (rp->r_todisgmt)
tod += gmtoff;
if (rp->r_todisstd && rp->r_stdoff == 0)
if (rp->r_todisstd && !rp->r_isdst)
tod += dstoff;
if (tod != 2 * SECSPERMIN * MINSPERHOUR)
{
@ -2536,7 +2614,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
continue;
if (rp->r_yrtype != NULL)
continue;
if (rp->r_stdoff == 0)
if (!rp->r_isdst)
{
if (stdrp == NULL)
stdrp = rp;
@ -2562,7 +2640,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
for (i = 0; i < zp->z_nrules; ++i)
{
rp = &zp->z_rules[i];
if (rp->r_stdoff == 0 && rule_cmp(stdabbrrp, rp) < 0)
if (!rp->r_isdst && rule_cmp(stdabbrrp, rp) < 0)
stdabbrrp = rp;
if (rule_cmp(stdrp, rp) < 0)
stdrp = rp;
@ -2576,7 +2654,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
if (stdrp != NULL && stdrp->r_hiyear == 2037)
return YEAR_BY_YEAR_ZONE;
if (stdrp != NULL && stdrp->r_stdoff != 0)
if (stdrp != NULL && stdrp->r_isdst)
{
/* Perpetual DST. */
dstr.r_month = TM_JANUARY;
@ -2584,6 +2662,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
dstr.r_dayofmonth = 1;
dstr.r_tod = 0;
dstr.r_todisstd = dstr.r_todisgmt = false;
dstr.r_isdst = stdrp->r_isdst;
dstr.r_stdoff = stdrp->r_stdoff;
dstr.r_abbrvar = stdrp->r_abbrvar;
stdr.r_month = TM_DECEMBER;
@ -2591,6 +2670,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
stdr.r_dayofmonth = 31;
stdr.r_tod = SECSPERDAY + stdrp->r_stdoff;
stdr.r_todisstd = stdr.r_todisgmt = false;
stdr.r_isdst = false;
stdr.r_stdoff = 0;
stdr.r_abbrvar
= (stdabbrrp ? stdabbrrp->r_abbrvar : "");
@ -2598,10 +2678,10 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
stdrp = &stdr;
}
}
if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_isdst))
return -1;
abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
len = doabbr(result, zp, abbrvar, 0, true);
len = doabbr(result, zp, abbrvar, false, 0, true);
offsetlen = stringoffset(result + len, -zp->z_gmtoff);
if (!offsetlen)
{
@ -2611,7 +2691,8 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
len += offsetlen;
if (dstrp == NULL)
return compat;
len += doabbr(result + len, zp, dstrp->r_abbrvar, dstrp->r_stdoff, true);
len += doabbr(result + len, zp, dstrp->r_abbrvar,
dstrp->r_isdst, dstrp->r_stdoff, true);
if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR)
{
offsetlen = stringoffset(result + len,
@ -2810,9 +2891,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
if (zp->z_nrules == 0)
{
stdoff = zp->z_stdoff;
doabbr(startbuf, zp, NULL, stdoff, false);
doabbr(startbuf, zp, NULL, zp->z_isdst, stdoff, false);
type = addtype(oadd(zp->z_gmtoff, stdoff),
startbuf, stdoff != 0, startttisstd,
startbuf, zp->z_isdst, startttisstd,
startttisgmt);
if (usestart)
{
@ -2927,6 +3008,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
stdoff);
doabbr(startbuf, zp,
rp->r_abbrvar,
rp->r_isdst,
rp->r_stdoff,
false);
continue;
@ -2938,6 +3020,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
doabbr(startbuf,
zp,
rp->r_abbrvar,
rp->r_isdst,
rp->r_stdoff,
false);
}
@ -2945,9 +3028,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
eats(zp->z_filename, zp->z_linenum,
rp->r_filename, rp->r_linenum);
doabbr(ab, zp, rp->r_abbrvar,
rp->r_stdoff, false);
rp->r_isdst, rp->r_stdoff, false);
offset = oadd(zp->z_gmtoff, rp->r_stdoff);
type = addtype(offset, ab, rp->r_stdoff != 0,
type = addtype(offset, ab, rp->r_isdst,
rp->r_todisstd, rp->r_todisgmt);
if (rp->r_hiyear == ZIC_MAX
&& !(0 <= lastatmax