From b45f6613e0a475f908d93dbaa8612ccb9395f666 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 4 May 2018 12:26:25 -0400 Subject: [PATCH] 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 --- src/timezone/README | 2 +- src/timezone/localtime.c | 14 ++-- src/timezone/strftime.c | 2 +- src/timezone/zic.c | 157 ++++++++++++++++++++++++++++++--------- 4 files changed, 129 insertions(+), 46 deletions(-) diff --git a/src/timezone/README b/src/timezone/README index 7b5573183d..2e41d7ac97 100644 --- a/src/timezone/README +++ b/src/timezone/README @@ -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. diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index 2b5b3a924f..31b06b037f 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -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. */ /* diff --git a/src/timezone/strftime.c b/src/timezone/strftime.c index bb638c81a4..275b5f477c 100644 --- a/src/timezone/strftime.c +++ b/src/timezone/strftime.c @@ -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; diff --git a/src/timezone/zic.c b/src/timezone/zic.c index 352e719329..66c5682925 100644 --- a/src/timezone/zic.c +++ b/src/timezone/zic.c @@ -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