]> granicus.if.org Git - postgresql/commitdiff
Sync our copy of the timezone library with IANA release tzcode2018e.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 May 2018 16:26:25 +0000 (12:26 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 May 2018 16:26:34 +0000 (12:26 -0400)
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
src/timezone/localtime.c
src/timezone/strftime.c
src/timezone/zic.c

index 7b5573183d493278487051c959b2cf0d31734c0e..2e41d7ac9779434520f90355f6d3df19b00793d4 100644 (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.
index 2b5b3a924fed34a89869bfdbd31642b080e7ccc1..31b06b037f4ffe6590df3e5deca3dfeba718643f 100644 (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.
                                         */
 
                                        /*
index bb638c81a45a28247b5f039231911b2080ca26f1..275b5f477c5f9cb556c1545c24908f43dd4db3d9 100644 (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;
index 352e7193299297b285f2089512e482cf3458211a..66c56829257e9ee2bc475eb23658303123abe435 100644 (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