From bd082231edbaf25626a023913394b611fe7928e8 Mon Sep 17 00:00:00 2001 From: Fujii Masao Date: Mon, 29 Aug 2016 16:06:40 +0900 Subject: Fix typos in comments. --- src/timezone/localtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/timezone/localtime.c') diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index a14215d6bd..d004e5ebe2 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -489,7 +489,7 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, } /* - * If type 0 is is unused in transitions, it's the type to use for early + * If type 0 is unused in transitions, it's the type to use for early * times. */ for (i = 0; i < sp->timecnt; ++i) -- cgit v1.2.3 From f3094920a567cde6c86adf36a1a033d7431b11ff Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 19 Oct 2016 18:55:52 -0400 Subject: Sync our copy of the timezone library with IANA release tzcode2016g. This is mostly to absorb some corner-case fixes in zic for year-2037 timestamps. The other changes that have been made are unlikely to affect our usage, but nonetheless we may as well take 'em. --- src/timezone/localtime.c | 5 +- src/timezone/private.h | 1 + src/timezone/strftime.c | 2 +- src/timezone/zic.c | 440 ++++++++++++++++++++++++++--------------------- 4 files changed, 250 insertions(+), 198 deletions(-) (limited to 'src/timezone/localtime.c') diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index d004e5ebe2..e6c1beaf96 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -1280,9 +1280,8 @@ gmtsub(pg_time_t const * timep, int32 offset, struct pg_tm * tmp) result = timesub(timep, offset, gmtptr, tmp); /* - * Could get fancy here and deliver something such as "UT+xxxx" or - * "UT-xxxx" if offset is non-zero, but this is no time for a treasure - * hunt. + * Could get fancy here and deliver something such as "+xx" or "-xx" if + * offset is non-zero, but this is no time for a treasure hunt. */ if (offset != 0) tmp->tm_zone = wildabbr; diff --git a/src/timezone/private.h b/src/timezone/private.h index 8480d38961..b8533d51e8 100644 --- a/src/timezone/private.h +++ b/src/timezone/private.h @@ -23,6 +23,7 @@ #include "pgtime.h" +/* This string was in the Factory zone through version 2016f. */ #define GRANDPARENTED "Local time zone must be set--see zic manual page" /* diff --git a/src/timezone/strftime.c b/src/timezone/strftime.c index 5630619321..4a0a01db65 100644 --- a/src/timezone/strftime.c +++ b/src/timezone/strftime.c @@ -128,7 +128,7 @@ pg_strftime(char *s, size_t maxsize, const char *format, int warn; warn = IN_NONE; - p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn); + p = _fmt(format, t, s, s + maxsize, &warn); if (p == s + maxsize) return 0; *p = '\0'; diff --git a/src/timezone/zic.c b/src/timezone/zic.c index b546a17372..52d8004ff7 100644 --- a/src/timezone/zic.c +++ b/src/timezone/zic.c @@ -105,7 +105,7 @@ static int addtype(zic_t, char const *, bool, bool, bool); static void leapadd(zic_t, bool, int, int); static void adjleap(void); static void associate(void); -static void dolink(const char *fromfield, const char *tofield); +static void dolink(const char *, const char *, bool); static char **getfields(char *buf); static zic_t gethms(const char *string, const char *errstring, bool); @@ -119,7 +119,7 @@ static bool inzsub(char **, int, bool); static int itsdir(const char *name); static bool is_alpha(char a); static char lowerit(char); -static bool mkdirs(char *); +static void mkdirs(char const *, bool); static void newabbr(const char *abbr); static zic_t oadd(zic_t t1, zic_t t2); static void outzone(const struct zone * zp, int ntzones); @@ -136,6 +136,15 @@ enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1}; +/* If true, work around a bug in Qt 5.6.1 and earlier, which mishandles + tz binary files whose POSIX-TZ-style strings contain '<'; see + QTBUG-53071 . This + workaround will no longer be needed when Qt 5.6.1 and earlier are + obsolete, say in the year 2021. */ +enum +{ +WORK_AROUND_QTBUG_53071 = true}; + static int charcnt; static bool errors; static bool warnings; @@ -346,6 +355,7 @@ static const int len_years[2] = { static struct attype { zic_t at; + bool dontmerge; unsigned char type; } *attypes; static zic_t gmtoffs[TZ_MAX_TYPES]; @@ -410,7 +420,8 @@ growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc) return ptr; else { - int amax = INT_MAX < SIZE_MAX ? INT_MAX : SIZE_MAX; + int nitems_max = INT_MAX - WORK_AROUND_QTBUG_53071; + int amax = nitems_max < SIZE_MAX ? nitems_max : SIZE_MAX; if ((amax - 1) / 3 * 2 < *nitems_alloc) memory_exhausted(_("int overflow")); @@ -478,17 +489,17 @@ warning(const char *string,...) } static void -close_file(FILE *stream, char const * name) +close_file(FILE *stream, char const * dir, char const * name) { char const *e = (ferror(stream) ? _("I/O error") : fclose(stream) != 0 ? strerror(errno) : NULL); if (e) { - fprintf(stderr, "%s: ", progname); - if (name) - fprintf(stderr, "%s: ", name); - fprintf(stderr, "%s\n", e); + fprintf(stderr, "%s: %s%s%s%s%s\n", progname, + dir ? dir : "", dir ? "/" : "", + name ? name : "", name ? ": " : "", + e); exit(EXIT_FAILURE); } } @@ -503,10 +514,34 @@ usage(FILE *stream, int status) "Report bugs to %s.\n"), progname, progname, PACKAGE_BUGREPORT); if (status == EXIT_SUCCESS) - close_file(stream, NULL); + close_file(stream, NULL, NULL); exit(status); } +/* Change the working directory to DIR, possibly creating DIR and its + ancestors. After this is done, all files are accessed with names + relative to DIR. */ +static void +change_directory(char const * dir) +{ + if (chdir(dir) != 0) + { + int chdir_errno = errno; + + if (chdir_errno == ENOENT) + { + mkdirs(dir, false); + chdir_errno = chdir(dir) == 0 ? 0 : errno; + } + if (chdir_errno != 0) + { + fprintf(stderr, _("%s: Can't chdir to %s: %s\n"), + progname, dir, strerror(chdir_errno)); + exit(EXIT_FAILURE); + } + } +} + static const char *psxrules; static const char *lcltime; static const char *directory; @@ -534,7 +569,7 @@ main(int argc, char *argv[]) if (strcmp(argv[i], "--version") == 0) { printf("zic %s\n", PG_VERSION); - close_file(stdout, NULL); + close_file(stdout, NULL, NULL); return EXIT_SUCCESS; } else if (strcmp(argv[i], "--help") == 0) @@ -630,6 +665,7 @@ main(int argc, char *argv[]) if (errors) return EXIT_FAILURE; associate(); + change_directory(directory); for (i = 0; i < nzones; i = j) { /* @@ -646,7 +682,7 @@ main(int argc, char *argv[]) for (i = 0; i < nlinks; ++i) { eat(links[i].l_filename, links[i].l_linenum); - dolink(links[i].l_from, links[i].l_to); + dolink(links[i].l_from, links[i].l_to, false); if (noise) for (j = 0; j < nlinks; ++j) if (strcmp(links[i].l_to, @@ -656,12 +692,12 @@ main(int argc, char *argv[]) if (lcltime != NULL) { eat(_("command line"), 1); - dolink(lcltime, TZDEFAULT); + dolink(lcltime, TZDEFAULT, true); } if (psxrules != NULL) { eat(_("command line"), 1); - dolink(psxrules, TZDEFRULES); + dolink(psxrules, TZDEFRULES, true); } if (warnings && (ferror(stderr) || fclose(stderr) != 0)) return EXIT_FAILURE; @@ -751,131 +787,117 @@ namecheck(const char *name) return componentcheck(name, component, cp); } -static char * -relname(char const * dir, char const * base) -{ - if (*base == '/') - return ecpyalloc(base); - else - { - size_t dir_len = strlen(dir); - bool needs_slash = dir_len && dir[dir_len - 1] != '/'; - char *result = emalloc(dir_len + needs_slash + strlen(base) + 1); - - result[dir_len] = '/'; - strcpy(result + dir_len + needs_slash, base); - return memcpy(result, dir, dir_len); - } -} - static void -dolink(char const * fromfield, char const * tofield) +dolink(char const * fromfield, char const * tofield, bool staysymlink) { - char *fromname; - char *toname; int fromisdir; - - fromname = relname(directory, fromfield); - toname = relname(directory, tofield); + bool todirs_made = false; + int link_errno; /* * We get to be careful here since there's a fair chance of root running * us. */ - fromisdir = itsdir(fromname); + fromisdir = itsdir(fromfield); if (fromisdir) { char const *e = strerror(fromisdir < 0 ? errno : EPERM); - fprintf(stderr, _("%s: link from %s failed: %s"), - progname, fromname, e); + fprintf(stderr, _("%s: link from %s/%s failed: %s\n"), + progname, directory, fromfield, e); exit(EXIT_FAILURE); } - if (link(fromname, toname) != 0) + if (staysymlink) + staysymlink = itsdir(tofield) == 2; + if (remove(tofield) == 0) + todirs_made = true; + else if (errno != ENOENT) { - int link_errno = errno; - bool retry_if_link_supported = false; + char const *e = strerror(errno); - if (link_errno == ENOENT || link_errno == ENOTSUP) + fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"), + progname, directory, tofield, e); + exit(EXIT_FAILURE); + } + link_errno = (staysymlink ? ENOTSUP + : link(fromfield, tofield) == 0 ? 0 : errno); + if (link_errno == ENOENT && !todirs_made) + { + mkdirs(tofield, true); + todirs_made = true; + link_errno = link(fromfield, tofield) == 0 ? 0 : errno; + } + if (link_errno != 0) + { +#ifdef HAVE_SYMLINK + const char *s = fromfield; + const char *t; + char *p; + size_t dotdots = 0; + char *symlinkcontents; + int symlink_errno; + + do + t = s; + while ((s = strchr(s, '/')) + && strncmp(fromfield, tofield, ++s - fromfield) == 0); + + for (s = tofield + (t - fromfield); *s; s++) + dotdots += *s == '/'; + symlinkcontents = emalloc(3 * dotdots + strlen(t) + 1); + for (p = symlinkcontents; dotdots-- != 0; p += 3) + memcpy(p, "../", 3); + strcpy(p, t); + symlink_errno = symlink(symlinkcontents, tofield) == 0 ? 0 : errno; + if (symlink_errno == ENOENT && !todirs_made) { - if (!mkdirs(toname)) - exit(EXIT_FAILURE); - retry_if_link_supported = true; + mkdirs(tofield, true); + symlink_errno = symlink(symlinkcontents, tofield) == 0 ? 0 : errno; } - if ((link_errno == EEXIST || link_errno == ENOTSUP) - && itsdir(toname) == 0 - && (remove(toname) == 0 || errno == ENOENT)) - retry_if_link_supported = true; - if (retry_if_link_supported && link_errno != ENOTSUP) - link_errno = link(fromname, toname) == 0 ? 0 : errno; - if (link_errno != 0) + free(symlinkcontents); + if (symlink_errno == 0) { -#ifdef HAVE_SYMLINK - const char *s = fromfield; - const char *t; - char *p; - size_t dotdots = 0; - char *symlinkcontents; - int symlink_result; - - do - t = s; - while ((s = strchr(s, '/')) - && strncmp(fromfield, tofield, ++s - fromfield) == 0); - - for (s = tofield + (t - fromfield); *s; s++) - dotdots += *s == '/'; - symlinkcontents = emalloc(3 * dotdots + strlen(t) + 1); - for (p = symlinkcontents; dotdots-- != 0; p += 3) - memcpy(p, "../", 3); - strcpy(p, t); - symlink_result = symlink(symlinkcontents, toname); - free(symlinkcontents); - if (symlink_result == 0) - { - if (link_errno != ENOTSUP) - warning(_("symbolic link used because hard link failed: %s"), - strerror(link_errno)); - } - else + if (link_errno != ENOTSUP) + warning(_("symbolic link used because hard link failed: %s"), + strerror(link_errno)); + } + else #endif /* HAVE_SYMLINK */ - { - FILE *fp, - *tp; - int c; + { + FILE *fp, + *tp; + int c; - fp = fopen(fromname, "rb"); - if (!fp) - { - const char *e = strerror(errno); + fp = fopen(fromfield, "rb"); + if (!fp) + { + char const *e = strerror(errno); - fprintf(stderr, - _("%s: Can't read %s: %s\n"), - progname, fromname, e); - exit(EXIT_FAILURE); - } - tp = fopen(toname, "wb"); - if (!tp) - { - const char *e = strerror(errno); + fprintf(stderr, _("%s: Can't read %s/%s: %s\n"), + progname, directory, fromfield, e); + exit(EXIT_FAILURE); + } + tp = fopen(tofield, "wb"); + if (!tp) + { + char const *e = strerror(errno); - fprintf(stderr, - _("%s: Can't create %s: %s\n"), - progname, toname, e); - exit(EXIT_FAILURE); - } - while ((c = getc(fp)) != EOF) - putc(c, tp); - close_file(fp, fromname); - close_file(tp, toname); - if (link_errno != ENOTSUP) - warning(_("copy used because hard link failed: %s"), - strerror(link_errno)); + fprintf(stderr, _("%s: Can't create %s/%s: %s\n"), + progname, directory, tofield, e); + exit(EXIT_FAILURE); } + while ((c = getc(fp)) != EOF) + putc(c, tp); + close_file(fp, directory, fromfield); + close_file(tp, directory, tofield); + if (link_errno != ENOTSUP) + warning(_("copy used because hard link failed: %s"), + strerror(link_errno)); + else if (symlink_errno != ENOTSUP) + warning(_("copy used because symbolic link failed: %s"), + strerror(symlink_errno)); } } - free(fromname); - free(toname); } #define TIME_T_BITS_IN_FILE 64 @@ -888,10 +910,6 @@ static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); * rounded downward to the negation of a power of two that is * comfortably outside the error bounds. * - * zic does not output time stamps before this, partly because they - * are physically suspect, and partly because GNOME mishandles them; see - * GNOME bug 730332 . - * * For the time of the Big Bang, see: * * Ade PAR, Aghanim N, Armitage-Caplan C et al. Planck 2013 results. @@ -913,26 +931,45 @@ static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); #define BIG_BANG (- (((zic_t) 1) << 59)) #endif -static const zic_t big_bang_time = BIG_BANG; +/* If true, work around GNOME bug 730332 + + by refusing to output time stamps before BIG_BANG. + Such time stamps are physically suspect anyway. -/* Return 1 if NAME is a directory, 0 if it's something else, -1 if trouble. */ + The GNOME bug is scheduled to be fixed in GNOME 3.22, and if so + this workaround will no longer be needed when GNOME 3.21 and + earlier are obsolete, say in the year 2021. */ +enum +{ +WORK_AROUND_GNOME_BUG_730332 = true}; + +static const zic_t early_time = (WORK_AROUND_GNOME_BUG_730332 + ? BIG_BANG + : MINVAL(zic_t, TIME_T_BITS_IN_FILE)); + +/* Return 1 if NAME is a directory, 2 if a symbolic link, 0 if + something else, -1 (setting errno) if trouble. */ static int itsdir(char const * name) { struct stat st; - int res = stat(name, &st); + int res = lstat(name, &st); -#ifdef S_ISDIR if (res == 0) - return S_ISDIR(st.st_mode) != 0; -#endif - if (res == 0 || errno == EOVERFLOW) { - char *nameslashdot = relname(name, "."); - bool dir = stat(nameslashdot, &st) == 0 || errno == EOVERFLOW; +#ifdef S_ISDIR + return S_ISDIR(st.st_mode) ? 1 : S_ISLNK(st.st_mode) ? 2 : 0; +#else + size_t n = strlen(name); + char *nameslashdot = emalloc(n + 3); + bool dir; + memcpy(nameslashdot, name, n); + strcpy(&nameslashdot[n], &"/."[!(n && name[n - 1] != '/')]); + dir = lstat(nameslashdot, &st) == 0; free(nameslashdot); return dir; +#endif } return -1; } @@ -1129,7 +1166,7 @@ infile(const char *name) } free(fields); } - close_file(fp, filename); + close_file(fp, NULL, filename); if (wantcont) error(_("expected continuation line not found")); } @@ -1313,7 +1350,7 @@ inzsub(char **fields, int nfields, bool iscont) z.z_filename = filename; z.z_linenum = linenum; z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"), true); - if ((cp = strchr(fields[i_format], '%')) != 0) + if ((cp = strchr(fields[i_format], '%')) != NULL) { if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%') || strchr(fields[i_format], '/')) @@ -1491,7 +1528,7 @@ inleap(char **fields, int nfields) return; } t = tadd(t, tod); - if (t < big_bang_time) + if (t < early_time) { error(_("leap second precedes Big Bang")); return; @@ -1764,11 +1801,14 @@ writezone(const char *const name, const char *const string, char version) int timecnt32, timei32; int pass; - char *fullname; static const struct tzhead tzh0; static struct tzhead tzh; - zic_t *ats = emalloc(size_product(timecnt, sizeof *ats + 1)); - void *typesptr = ats + timecnt; + bool dir_checked = false; + zic_t one = 1; + zic_t y2038_boundary = one << 31; + int nats = timecnt + WORK_AROUND_QTBUG_53071; + zic_t *ats = emalloc(size_product(nats, sizeof *ats + 1)); + void *typesptr = ats + nats; unsigned char *types = typesptr; /* @@ -1786,7 +1826,7 @@ writezone(const char *const name, const char *const string, char version) toi = 0; fromi = 0; - while (fromi < timecnt && attypes[fromi].at < big_bang_time) + while (fromi < timecnt && attypes[fromi].at < early_time) ++fromi; for (; fromi < timecnt; ++fromi) { @@ -1799,8 +1839,9 @@ writezone(const char *const name, const char *const string, char version) attypes[fromi].type; continue; } - if (toi == 0 || - attypes[toi - 1].type != attypes[fromi].type) + if (toi == 0 + || attypes[fromi].dontmerge + || attypes[toi - 1].type != attypes[fromi].type) attypes[toi++] = attypes[fromi]; } timecnt = toi; @@ -1818,6 +1859,20 @@ writezone(const char *const name, const char *const string, char version) types[i] = attypes[i].type; } + /* + * Work around QTBUG-53071 for time stamps less than y2038_boundary - 1, + * by inserting a no-op transition at time y2038_boundary - 1. This works + * only for timestamps before the boundary, which should be good enough in + * practice as QTBUG-53071 should be long-dead by 2038. + */ + if (WORK_AROUND_QTBUG_53071 && timecnt != 0 + && ats[timecnt - 1] < y2038_boundary - 1 && strchr(string, '<')) + { + ats[timecnt] = y2038_boundary - 1; + types[timecnt] = types[timecnt - 1]; + timecnt++; + } + /* * Correct for leap seconds. */ @@ -1862,29 +1917,35 @@ writezone(const char *const name, const char *const string, char version) --leapcnt32; ++leapi32; } - fullname = relname(directory, name); /* * Remove old file, if any, to snap links. */ - if (itsdir(fullname) == 0 && remove(fullname) != 0 && errno != ENOENT) + if (remove(name) == 0) + dir_checked = true; + else if (errno != ENOENT) { const char *e = strerror(errno); - fprintf(stderr, _("%s: Cannot remove %s: %s\n"), - progname, fullname, e); + fprintf(stderr, _("%s: Cannot remove %s/%s: %s\n"), + progname, directory, name, e); exit(EXIT_FAILURE); } - if ((fp = fopen(fullname, "wb")) == NULL) + fp = fopen(name, "wb"); + if (!fp) { - if (!mkdirs(fullname)) - exit(EXIT_FAILURE); - if ((fp = fopen(fullname, "wb")) == NULL) - { - const char *e = strerror(errno); + int fopen_errno = errno; - fprintf(stderr, _("%s: Cannot create %s: %s\n"), - progname, fullname, e); + if (fopen_errno == ENOENT && !dir_checked) + { + mkdirs(name, true); + fp = fopen(name, "wb"); + fopen_errno = errno; + } + if (!fp) + { + fprintf(stderr, _("%s: Cannot create %s/%s: %s\n"), + progname, directory, name, strerror(fopen_errno)); exit(EXIT_FAILURE); } } @@ -2130,9 +2191,8 @@ writezone(const char *const name, const char *const string, char version) putc(ttisgmts[i], fp); } fprintf(fp, "\n%s\n", string); - close_file(fp, fullname); + close_file(fp, directory, name); free(ats); - free(fullname); } static char const * @@ -2527,6 +2587,7 @@ outzone(const struct zone * zpfirst, int zonecount) int compat; bool do_extend; char version; + int lastatmax = -1; max_abbr_len = 2 + max_format_len + max_abbrvar_len; max_envvar_len = 2 * max_abbr_len + 5 * 9; @@ -2649,9 +2710,9 @@ outzone(const struct zone * zpfirst, int zonecount) */ stdoff = 0; zp = &zpfirst[i]; - usestart = i > 0 && (zp - 1)->z_untiltime > big_bang_time; + usestart = i > 0 && (zp - 1)->z_untiltime > early_time; useuntil = i < (zonecount - 1); - if (useuntil && zp->z_untiltime <= big_bang_time) + if (useuntil && zp->z_untiltime <= early_time) continue; gmtoff = zp->z_gmtoff; eat(zp->z_filename, zp->z_linenum); @@ -2670,7 +2731,7 @@ outzone(const struct zone * zpfirst, int zonecount) usestart = false; } else - addtt(big_bang_time, type); + addtt(early_time, type); } else for (year = min_year; year <= max_year; ++year) @@ -2792,6 +2853,10 @@ outzone(const struct zone * zpfirst, int zonecount) offset = oadd(zp->z_gmtoff, rp->r_stdoff); type = addtype(offset, ab, rp->r_stdoff != 0, rp->r_todisstd, rp->r_todisgmt); + if (rp->r_hiyear == ZIC_MAX + && !(0 <= lastatmax + && ktime < attypes[lastatmax].at)) + lastatmax = timecnt; addtt(ktime, type); } } @@ -2827,6 +2892,8 @@ outzone(const struct zone * zpfirst, int zonecount) starttime = tadd(starttime, -gmtoff); } } + if (0 <= lastatmax) + attypes[lastatmax].dontmerge = true; if (do_extend) { /* @@ -2850,22 +2917,8 @@ outzone(const struct zone * zpfirst, int zonecount) lastat = &attypes[i]; if (lastat->at < rpytime(&xr, max_year - 1)) { - /* - * Create new type code for the redundant entry, to prevent it - * being optimized away. - */ - if (typecnt >= TZ_MAX_TYPES) - { - error(_("too many local time types")); - exit(EXIT_FAILURE); - } - gmtoffs[typecnt] = gmtoffs[lastat->type]; - isdsts[typecnt] = isdsts[lastat->type]; - ttisstds[typecnt] = ttisstds[lastat->type]; - ttisgmts[typecnt] = ttisgmts[lastat->type]; - abbrinds[typecnt] = abbrinds[lastat->type]; - ++typecnt; addtt(rpytime(&xr, max_year + 1), typecnt - 1); + attypes[timecnt - 1].dontmerge = true; } } writezone(zpfirst->z_name, envvar, version); @@ -2877,8 +2930,8 @@ outzone(const struct zone * zpfirst, int zonecount) static void addtt(zic_t starttime, int type) { - if (starttime <= big_bang_time || - (timecnt == 1 && attypes[0].at < big_bang_time)) + if (starttime <= early_time + || (timecnt == 1 && attypes[0].at < early_time)) { gmtoffs[0] = gmtoffs[type]; isdsts[0] = isdsts[type]; @@ -2894,6 +2947,7 @@ addtt(zic_t starttime, int type) } attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc); attypes[timecnt].at = starttime; + attypes[timecnt].dontmerge = false; attypes[timecnt].type = type; ++timecnt; } @@ -3441,53 +3495,51 @@ newabbr(const char *string) charcnt += i; } -static bool -mkdirs(char *argname) +/* Ensure that the directories of ARGNAME exist, by making any missing + ones. If ANCESTORS, do this only for ARGNAME's ancestors; otherwise, + do it for ARGNAME too. Exit with failure if there is trouble. */ +static void +mkdirs(char const * argname, bool ancestors) { char *name; char *cp; - if (argname == NULL || *argname == '\0') - return true; cp = name = ecpyalloc(argname); - while ((cp = strchr(cp + 1, '/')) != NULL) - { - *cp = '\0'; + + /* Do not mkdir a root directory, as it must exist. */ #ifdef WIN32 + if (is_alpha(name[0]) && name[1] == ':') + cp += 2; +#endif + while (*cp == '/') + cp++; - /* - * DOS drive specifier? - */ - if (is_alpha(name[0]) && name[1] == ':' && name[2] == '\0') - { - *cp = '/'; - continue; - } -#endif /* WIN32 */ + while (cp && ((cp = strchr(cp, '/')) || !ancestors)) + { + if (cp) + *cp = '\0'; /* * Try to create it. It's OK if creation fails because the directory * already exists, perhaps because some other process just created it. + * For simplicity do not check first whether it already exists, as + * that is checked anyway if the mkdir fails. */ if (mkdir(name, MKDIR_UMASK) != 0) { int err = errno; - if (itsdir(name) <= 0) + if (err != EEXIST && itsdir(name) < 0) { - char const *e = strerror(err); - - warning(_("%s: Can't create directory" - " %s: %s"), - progname, name, e); - free(name); - return false; + error(_("%s: Cannot create directory %s: %s"), + progname, name, strerror(err)); + exit(EXIT_FAILURE); } } - *cp = '/'; + if (cp) + *cp++ = '/'; } free(name); - return true; } -- cgit v1.2.3 From e18b2c480da478f62781e06488cda56fe1b4e919 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 30 Apr 2017 15:13:51 -0400 Subject: Sync our copy of the timezone library with IANA release tzcode2017b. zic no longer mishandles some transitions in January 2038 when it attempts to work around Qt bug 53071. This fixes a bug affecting Pacific/Tongatapu that was introduced in zic 2016e. localtime.c now contains a workaround, useful when loading a file generated by a buggy zic. There are assorted cosmetic changes as well, notably relocation of a bunch of #defines. --- src/timezone/README | 2 +- src/timezone/localtime.c | 73 +++++++++++++++++++++++++++++++++++----------- src/timezone/pgtz.c | 9 +++--- src/timezone/private.h | 76 ++++++++++++++++++++++++++++++++++++------------ src/timezone/strftime.c | 11 +++++-- src/timezone/tzfile.h | 52 --------------------------------- src/timezone/zic.c | 15 ++++++++-- 7 files changed, 139 insertions(+), 99 deletions(-) (limited to 'src/timezone/localtime.c') diff --git a/src/timezone/README b/src/timezone/README index a82d77c7ba..2544230c4c 100644 --- a/src/timezone/README +++ b/src/timezone/README @@ -50,7 +50,7 @@ match properly on the old version. Time Zone code ============== -The code in this directory is currently synced with tzcode release 2016j. +The code in this directory is currently synced with tzcode release 2017b. 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 e6c1beaf96..154124e49c 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -17,8 +17,9 @@ #include #include "datatype/timestamp.h" -#include "private.h" #include "pgtz.h" + +#include "private.h" #include "tzfile.h" @@ -414,10 +415,10 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, { /* * Attempt to reuse existing abbreviations. Without this, - * America/Anchorage would stop working after 2037 when - * TZ_MAX_CHARS is 50, as sp->charcnt equals 42 (for LMT CAT CAWT - * CAPT AHST AHDT YST AKDT AKST) and ts->charcnt equals 10 (for - * AKST AKDT). Reusing means sp->charcnt can stay 42 in this + * America/Anchorage would be right on the edge after 2037 when + * TZ_MAX_CHARS is 50, as sp->charcnt equals 40 (for LMT AST AWT + * APT AHST AHDT YST AKDT AKST) and ts->charcnt equals 10 (for + * AKST AKDT). Reusing means sp->charcnt can stay 40 in this * example. */ int gotabbr = 0; @@ -451,6 +452,17 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, if (gotabbr == 2) { sp->charcnt = charcnt; + + /* + * Ignore any trailing, no-op transitions generated by zic as + * they don't help here and can run afoul of bugs in zic 2016j + * or earlier. + */ + while (1 < sp->timecnt + && (sp->types[sp->timecnt - 1] + == sp->types[sp->timecnt - 2])) + sp->timecnt--; + for (i = 0; i < ts->timecnt; i++) if (sp->ats[sp->timecnt - 1] < ts->ats[i]) break; @@ -974,6 +986,8 @@ tzparse(const char *name, struct state * sp, bool lastditch) int yearlim; int timecnt; pg_time_t janfirst; + int32 janoffset = 0; + int yearbeg; ++name; if ((name = getrule(name, &start)) == NULL) @@ -994,8 +1008,23 @@ tzparse(const char *name, struct state * sp, bool lastditch) sp->defaulttype = 0; timecnt = 0; janfirst = 0; - yearlim = EPOCH_YEAR + YEARSPERREPEAT; - for (year = EPOCH_YEAR; year < yearlim; year++) + yearbeg = EPOCH_YEAR; + + do + { + int32 yearsecs + = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY; + + yearbeg--; + if (increment_overflow_time(&janfirst, -yearsecs)) + { + janoffset = -yearsecs; + break; + } + } while (EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg); + + yearlim = yearbeg + YEARSPERREPEAT + 1; + for (year = yearbeg; year < yearlim; year++) { int32 starttime = transtime(year, &start, stdoffset), @@ -1020,24 +1049,34 @@ tzparse(const char *name, struct state * sp, bool lastditch) { if (TZ_MAX_TIMES - 2 < timecnt) break; - yearlim = year + YEARSPERREPEAT + 1; sp->ats[timecnt] = janfirst; - if (increment_overflow_time - (&sp->ats[timecnt], starttime)) - break; - sp->types[timecnt++] = reversed; + if (!increment_overflow_time + (&sp->ats[timecnt], + janoffset + starttime)) + sp->types[timecnt++] = reversed; + else if (janoffset) + sp->defaulttype = reversed; sp->ats[timecnt] = janfirst; - if (increment_overflow_time - (&sp->ats[timecnt], endtime)) - break; - sp->types[timecnt++] = !reversed; + if (!increment_overflow_time + (&sp->ats[timecnt], + janoffset + endtime)) + { + sp->types[timecnt++] = !reversed; + yearlim = year + YEARSPERREPEAT + 1; + } + else if (janoffset) + sp->defaulttype = !reversed; } - if (increment_overflow_time(&janfirst, yearsecs)) + if (increment_overflow_time + (&janfirst, janoffset + yearsecs)) break; + janoffset = 0; } sp->timecnt = timecnt; if (!timecnt) sp->typecnt = 1; /* Perpetual DST. */ + else if (YEARSPERREPEAT < year - yearbeg) + sp->goback = sp->goahead = true; } else { diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c index b7cfc5f5ac..13ce399033 100644 --- a/src/timezone/pgtz.c +++ b/src/timezone/pgtz.c @@ -17,6 +17,7 @@ #include #include +#include "datatype/timestamp.h" #include "miscadmin.h" #include "pgtz.h" #include "storage/fd.h" @@ -308,14 +309,14 @@ pg_tzset_offset(long gmtoffset) char tzname[128]; snprintf(offsetstr, sizeof(offsetstr), - "%02ld", absoffset / SECSPERHOUR); - absoffset %= SECSPERHOUR; + "%02ld", absoffset / SECS_PER_HOUR); + absoffset %= SECS_PER_HOUR; if (absoffset != 0) { snprintf(offsetstr + strlen(offsetstr), sizeof(offsetstr) - strlen(offsetstr), - ":%02ld", absoffset / SECSPERMIN); - absoffset %= SECSPERMIN; + ":%02ld", absoffset / SECS_PER_MINUTE); + absoffset %= SECS_PER_MINUTE; if (absoffset != 0) snprintf(offsetstr + strlen(offsetstr), sizeof(offsetstr) - strlen(offsetstr), diff --git a/src/timezone/private.h b/src/timezone/private.h index b8533d51e8..f031b17b7e 100644 --- a/src/timezone/private.h +++ b/src/timezone/private.h @@ -67,14 +67,8 @@ extern int unlink(const char *filename); * Finally, some convenience items. */ -#ifndef TYPE_BIT #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) -#endif /* !defined TYPE_BIT */ - -#ifndef TYPE_SIGNED #define TYPE_SIGNED(type) (((type) -1) < 0) -#endif /* !defined TYPE_SIGNED */ - #define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0) /* @@ -88,7 +82,6 @@ extern int unlink(const char *filename); #define MINVAL(t, b) \ ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0)) -#ifndef INT_STRLEN_MAXIMUM /* * 302 / 1000 is log10(2.0) rounded up. * Subtract one for the sign bit if the type is signed; @@ -98,7 +91,6 @@ extern int unlink(const char *filename); #define INT_STRLEN_MAXIMUM(type) \ ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ 1 + TYPE_SIGNED(type)) -#endif /* !defined INT_STRLEN_MAXIMUM */ /* * INITIALIZE(x) @@ -108,24 +100,70 @@ extern int unlink(const char *filename); #undef _ #define _(msgid) (msgid) -#ifndef YEARSPERREPEAT +/* Handy macros that are independent of tzfile implementation. */ + #define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ -#endif /* !defined YEARSPERREPEAT */ + +#define SECSPERMIN 60 +#define MINSPERHOUR 60 +#define HOURSPERDAY 24 +#define DAYSPERWEEK 7 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY ((int32) SECSPERHOUR * HOURSPERDAY) +#define MONSPERYEAR 12 + +#define TM_SUNDAY 0 +#define TM_MONDAY 1 +#define TM_TUESDAY 2 +#define TM_WEDNESDAY 3 +#define TM_THURSDAY 4 +#define TM_FRIDAY 5 +#define TM_SATURDAY 6 + +#define TM_JANUARY 0 +#define TM_FEBRUARY 1 +#define TM_MARCH 2 +#define TM_APRIL 3 +#define TM_MAY 4 +#define TM_JUNE 5 +#define TM_JULY 6 +#define TM_AUGUST 7 +#define TM_SEPTEMBER 8 +#define TM_OCTOBER 9 +#define TM_NOVEMBER 10 +#define TM_DECEMBER 11 + +#define TM_YEAR_BASE 1900 + +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY TM_THURSDAY + +#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) /* - * The Gregorian year averages 365.2425 days, which is 31556952 seconds. + * Since everything in isleap is modulo 400 (or a factor of 400), we know that + * isleap(y) == isleap(y % 400) + * and so + * isleap(a + b) == isleap((a + b) % 400) + * or + * isleap(a + b) == isleap(a % 400 + b % 400) + * This is true even if % means modulo rather than Fortran remainder + * (which is allowed by C89 but not C99). + * We use this to avoid addition overflow problems. */ -#ifndef AVGSECSPERYEAR -#define AVGSECSPERYEAR 31556952L -#endif /* !defined AVGSECSPERYEAR */ +#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) -#ifndef SECSPERREPEAT -#define SECSPERREPEAT ((int64) YEARSPERREPEAT * (int64) AVGSECSPERYEAR) -#endif /* !defined SECSPERREPEAT */ -#ifndef SECSPERREPEAT_BITS +/* + * The Gregorian year averages 365.2425 days, which is 31556952 seconds. + */ + +#define AVGSECSPERYEAR 31556952L +#define SECSPERREPEAT \ + ((int64) YEARSPERREPEAT * (int64) AVGSECSPERYEAR) #define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ -#endif /* !defined SECSPERREPEAT_BITS */ #endif /* !defined PRIVATE_H */ diff --git a/src/timezone/strftime.c b/src/timezone/strftime.c index acaba86881..d9a18d49a8 100644 --- a/src/timezone/strftime.c +++ b/src/timezone/strftime.c @@ -1,4 +1,4 @@ -/* Convert a broken-down time stamp to a string. */ +/* Convert a broken-down timestamp to a string. */ /* * Copyright 1989 The Regents of the University of California. @@ -43,7 +43,6 @@ #include #include "private.h" -#include "tzfile.h" struct lc_time_T @@ -451,11 +450,17 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, { long diff; char const *sign; + bool negative; if (t->tm_isdst < 0) continue; diff = t->tm_gmtoff; - if (diff < 0) + negative = diff < 0; + if (diff == 0) + { + negative = t->tm_zone[0] == '-'; + } + if (negative) { sign = "-"; diff = -diff; diff --git a/src/timezone/tzfile.h b/src/timezone/tzfile.h index 32d237b827..56a5b43472 100644 --- a/src/timezone/tzfile.h +++ b/src/timezone/tzfile.h @@ -100,56 +100,4 @@ struct tzhead #define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ -#define SECSPERMIN 60 -#define MINSPERHOUR 60 -#define HOURSPERDAY 24 -#define DAYSPERWEEK 7 -#define DAYSPERNYEAR 365 -#define DAYSPERLYEAR 366 -#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) -#define SECSPERDAY ((int32) SECSPERHOUR * HOURSPERDAY) -#define MONSPERYEAR 12 - -#define TM_SUNDAY 0 -#define TM_MONDAY 1 -#define TM_TUESDAY 2 -#define TM_WEDNESDAY 3 -#define TM_THURSDAY 4 -#define TM_FRIDAY 5 -#define TM_SATURDAY 6 - -#define TM_JANUARY 0 -#define TM_FEBRUARY 1 -#define TM_MARCH 2 -#define TM_APRIL 3 -#define TM_MAY 4 -#define TM_JUNE 5 -#define TM_JULY 6 -#define TM_AUGUST 7 -#define TM_SEPTEMBER 8 -#define TM_OCTOBER 9 -#define TM_NOVEMBER 10 -#define TM_DECEMBER 11 - -#define TM_YEAR_BASE 1900 - -#define EPOCH_YEAR 1970 -#define EPOCH_WDAY TM_THURSDAY - -#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) - -/* - * Since everything in isleap is modulo 400 (or a factor of 400), we know that - * isleap(y) == isleap(y % 400) - * and so - * isleap(a + b) == isleap((a + b) % 400) - * or - * isleap(a + b) == isleap(a % 400 + b % 400) - * This is true even if % means modulo rather than Fortran remainder - * (which is allowed by C89 but not C99). - * We use this to avoid addition overflow problems. - */ - -#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) - #endif /* !defined TZFILE_H */ diff --git a/src/timezone/zic.c b/src/timezone/zic.c index e1875585df..5fa0a81262 100644 --- a/src/timezone/zic.c +++ b/src/timezone/zic.c @@ -2671,6 +2671,9 @@ outzone(const struct zone * zpfirst, ptrdiff_t zonecount) bool do_extend; char version; ptrdiff_t lastatmax = -1; + zic_t one = 1; + zic_t y2038_boundary = one << 31; + zic_t max_year0; max_abbr_len = 2 + max_format_len + max_abbrvar_len; max_envvar_len = 2 * max_abbr_len + 5 * 9; @@ -2780,12 +2783,13 @@ outzone(const struct zone * zpfirst, ptrdiff_t zonecount) } /* - * For the benefit of older systems, generate data from 1900 through 2037. + * For the benefit of older systems, generate data from 1900 through 2038. */ if (min_year > 1900) min_year = 1900; - if (max_year < 2037) - max_year = 2037; + max_year0 = max_year; + if (max_year < 2038) + max_year = 2038; for (i = 0; i < zonecount; ++i) { /* @@ -2835,7 +2839,12 @@ outzone(const struct zone * zpfirst, ptrdiff_t zonecount) year <= rp->r_hiyear && yearistype(year, rp->r_yrtype); if (rp->r_todo) + { rp->r_temp = rpytime(rp, year); + rp->r_todo + = (rp->r_temp < y2038_boundary + || year <= max_year0); + } } for (;;) { -- cgit v1.2.3 From af2c5aa88d38573724e40fa029499b4db20b0eb2 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 2 May 2017 21:50:35 -0400 Subject: Improve performance of timezone loading, especially pg_timezone_names view. tzparse() would attempt to load the "posixrules" timezone database file on each call. That might seem like it would only be an issue when selecting a POSIX-style zone name rather than a zone defined in the timezone database, but it turns out that each zone definition file contains a POSIX-style zone string and tzload() will call tzparse() to parse that. Thus, when scanning the whole timezone file tree as we do in the pg_timezone_names view, "posixrules" was read repetitively for each zone definition file. Fix that by caching the file on first use within any given process. (We cache other zone definitions for the life of the process, so there seems little reason not to cache this one as well.) This probably won't help much in processes that never run pg_timezone_names, but even one additional SET of the timezone GUC would come out ahead. An even worse problem for pg_timezone_names is that pg_open_tzfile() has an inefficient way of identifying the canonical case of a zone name: it basically re-descends the directory tree to the zone file. That's not awful for an individual "SET timezone" operation, but it's pretty horrid when we're inspecting every zone in the database. And it's pointless too because we already know the canonical spelling, having just read it from the filesystem. Fix by teaching pg_open_tzfile() to avoid the directory search if it's not asked for the canonical name, and backfilling the proper result in pg_tzenumerate_next(). In combination these changes seem to make the pg_timezone_names view about 3x faster to read, for me. Since a scan of pg_timezone_names has up to now been one of the slowest queries in the regression tests, this should help some little bit for buildfarm cycle times. Back-patch to all supported branches, not so much because it's likely that users will care much about the view's performance as because tracking changes in the upstream IANA timezone code is really painful if we don't keep all the branches in sync. Discussion: https://postgr.es/m/27962.1493671706@sss.pgh.pa.us --- src/timezone/README | 3 +++ src/timezone/localtime.c | 23 ++++++++++++++++++++++- src/timezone/pgtz.c | 42 +++++++++++++++++++++++++++++++++++------- 3 files changed, 60 insertions(+), 8 deletions(-) (limited to 'src/timezone/localtime.c') diff --git a/src/timezone/README b/src/timezone/README index 2544230c4c..912e0c1b39 100644 --- a/src/timezone/README +++ b/src/timezone/README @@ -79,6 +79,9 @@ other exposed names. slightly modified the API of the former, in part because it now relies on our own pg_open_tzfile() rather than opening files for itself. +* tzparse() is adjusted to cache the result of loading the TZDEFRULES +zone, so that that's not repeated more than once per process. + * There's a fair amount of code we don't need and have removed, including all the nonstandard optional APIs. We have also added a few functions of our own at the bottom of localtime.c. diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index 154124e49c..a2faa82ac8 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -54,6 +54,13 @@ static const char gmt[] = "GMT"; static const pg_time_t time_t_min = MINVAL(pg_time_t, TYPE_BIT(pg_time_t)); static const pg_time_t time_t_max = MAXVAL(pg_time_t, TYPE_BIT(pg_time_t)); +/* + * We cache the result of trying to load the TZDEFRULES zone here. + * tzdefrules_loaded is 0 if not tried yet, +1 if good, -1 if failed. + */ +static struct state tzdefrules_s; +static int tzdefrules_loaded = 0; + /* * The DST rules to use if TZ has no rules and we can't load TZDEFRULES. * We default to US rules as of 1999-08-17. @@ -942,7 +949,21 @@ tzparse(const char *name, struct state * sp, bool lastditch) charcnt = stdlen + 1; if (sizeof sp->chars < charcnt) return false; - load_ok = tzload(TZDEFRULES, NULL, sp, false) == 0; + + /* + * This bit also differs from the IANA code, which doesn't make any + * attempt to avoid repetitive loadings of the TZDEFRULES zone. + */ + if (tzdefrules_loaded == 0) + { + if (tzload(TZDEFRULES, NULL, &tzdefrules_s, false) == 0) + tzdefrules_loaded = 1; + else + tzdefrules_loaded = -1; + } + load_ok = (tzdefrules_loaded > 0); + if (load_ok) + memcpy(sp, &tzdefrules_s, sizeof(struct state)); } if (!load_ok) sp->leapcnt = 0; /* so, we're off a little */ diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c index 13ce399033..31dbf8993b 100644 --- a/src/timezone/pgtz.c +++ b/src/timezone/pgtz.c @@ -80,12 +80,37 @@ pg_open_tzfile(const char *name, char *canonname) int fullnamelen; int orignamelen; + /* Initialize fullname with base name of tzdata directory */ + strlcpy(fullname, pg_TZDIR(), sizeof(fullname)); + orignamelen = fullnamelen = strlen(fullname); + + if (fullnamelen + 1 + strlen(name) >= MAXPGPATH) + return -1; /* not gonna fit */ + + /* + * If the caller doesn't need the canonical spelling, first just try to + * open the name as-is. This can be expected to succeed if the given name + * is already case-correct, or if the filesystem is case-insensitive; and + * we don't need to distinguish those situations if we aren't tasked with + * reporting the canonical spelling. + */ + if (canonname == NULL) + { + int result; + + fullname[fullnamelen] = '/'; + /* test above ensured this will fit: */ + strcpy(fullname + fullnamelen + 1, name); + result = open(fullname, O_RDONLY | PG_BINARY, 0); + if (result >= 0) + return result; + /* If that didn't work, fall through to do it the hard way */ + } + /* * Loop to split the given name into directory levels; for each level, * search using scan_directory_ci(). */ - strlcpy(fullname, pg_TZDIR(), sizeof(fullname)); - orignamelen = fullnamelen = strlen(fullname); fname = name; for (;;) { @@ -97,8 +122,6 @@ pg_open_tzfile(const char *name, char *canonname) fnamelen = slashptr - fname; else fnamelen = strlen(fname); - if (fullnamelen + 1 + fnamelen >= MAXPGPATH) - return -1; /* not gonna fit */ if (!scan_directory_ci(fullname, fname, fnamelen, fullname + fullnamelen + 1, MAXPGPATH - fullnamelen - 1)) @@ -458,10 +481,11 @@ pg_tzenumerate_next(pg_tzenum *dir) /* * Load this timezone using tzload() not pg_tzset(), so we don't fill - * the cache + * the cache. Also, don't ask for the canonical spelling: we already + * know it, and pg_open_tzfile's way of finding it out is pretty + * inefficient. */ - if (tzload(fullname + dir->baselen, dir->tz.TZname, &dir->tz.state, - true) != 0) + if (tzload(fullname + dir->baselen, NULL, &dir->tz.state, true) != 0) { /* Zone could not be loaded, ignore it */ continue; @@ -473,6 +497,10 @@ pg_tzenumerate_next(pg_tzenum *dir) continue; } + /* OK, return the canonical zone name spelling. */ + strlcpy(dir->tz.TZname, fullname + dir->baselen, + sizeof(dir->tz.TZname)); + /* Timezone loaded OK. */ return &dir->tz; } -- cgit v1.2.3