1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

Sync our copy of the timezone library with IANA tzcode master.

This patch absorbs a few unreleased fixes in the IANA code.
It corresponds to commit 2d8b944c1cec0808ac4f7a9ee1a463c28f9cd00a
in https://github.com/eggert/tz.  Non-cosmetic changes include:

TZDEFRULESTRING is updated to match current US DST practice,
rather than what it was over ten years ago.  This only matters
for interpretation of POSIX-style zone names (e.g., "EST5EDT"),
and only if the timezone database doesn't include either an exact
match for the zone name or a "posixrules" entry.  The latter
should not be true in any current Postgres installation, but
this could possibly matter when using --with-system-tzdata.

Get rid of a nonportable use of "++var" on a bool var.
This is part of a larger fix that eliminates some vestigial
support for consecutive leap seconds, and adds checks to
the "zic" compiler that the data files do not specify that.

Remove a couple of ancient compatibility hacks.  The IANA
crew think these are obsolete, and I tend to agree.  But
perhaps our buildfarm will think different.

Back-patch to all supported branches, in line with our policy
that all branches should be using current IANA code.  Before v10,
this includes application of current pgindent rules, to avoid
whitespace problems in future back-patches.

Discussion: https://postgr.es/m/E1dsWhf-0000pT-F9@gemulon.postgresql.org
This commit is contained in:
Tom Lane
2017-09-22 00:04:21 -04:00
parent ea31541f56
commit e25f4401da
5 changed files with 236 additions and 204 deletions

View File

@ -9,10 +9,8 @@
#include "postgres_fe.h"
#include <fcntl.h>
#include <locale.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "pg_getopt.h"
@ -28,7 +26,7 @@ typedef int64 zic_t;
#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
#define ZIC_MAX_ABBR_LEN_WO_WARN 6
#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
#ifndef WIN32
#ifdef S_IRUSR
@ -47,9 +45,12 @@ typedef int64 zic_t;
static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
#endif
/* The type and printf format for line numbers. */
/*
* The type for line numbers. In Postgres, use %d to format them; upstream
* uses PRIdMAX but we prefer not to rely on that, not least because it
* results in platform-dependent strings to be translated.
*/
typedef int lineno_t;
#define PRIdLINENO "d"
struct rule
{
@ -84,9 +85,9 @@ struct rule
* r_dycode r_dayofmonth r_wday
*/
#define DC_DOM 0 /* 1..31 */ /* unused */
#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */
#define DC_DOM 0 /* 1..31 */ /* unused */
#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */
struct zone
{
@ -138,9 +139,9 @@ static char lowerit(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, ptrdiff_t ntzones);
static zic_t rpytime(const struct rule * rp, zic_t wantedy);
static void rulesub(struct rule * rp,
static void outzone(const struct zone *zp, ptrdiff_t ntzones);
static zic_t rpytime(const struct rule *rp, zic_t wantedy);
static void rulesub(struct rule *rp,
const char *loyearp, const char *hiyearp,
const char *typep, const char *monthp,
const char *dayp, const char *timep);
@ -292,12 +293,15 @@ struct lookup
};
static struct lookup const *byword(const char *string,
const struct lookup * lp);
const struct lookup *lp);
static struct lookup const line_codes[] = {
static struct lookup const zi_line_codes[] = {
{"Rule", LC_RULE},
{"Zone", LC_ZONE},
{"Link", LC_LINK},
{NULL, 0}
};
static struct lookup const leap_line_codes[] = {
{"Leap", LC_LEAP},
{NULL, 0}
};
@ -373,7 +377,7 @@ static struct attype
zic_t at;
bool dontmerge;
unsigned char type;
} *attypes;
} *attypes;
static zic_t gmtoffs[TZ_MAX_TYPES];
static char isdsts[TZ_MAX_TYPES];
static unsigned char abbrinds[TZ_MAX_TYPES];
@ -424,7 +428,7 @@ erealloc(void *ptr, size_t size)
}
static char *
ecpyalloc(char const * str)
ecpyalloc(char const *str)
{
return memcheck(strdup(str));
}
@ -436,7 +440,8 @@ growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
return ptr;
else
{
ptrdiff_t amax = PTRDIFF_MAX - WORK_AROUND_QTBUG_53071;
ptrdiff_t nitems_max = PTRDIFF_MAX - WORK_AROUND_QTBUG_53071;
ptrdiff_t amax = nitems_max < SIZE_MAX ? nitems_max : SIZE_MAX;
if ((amax - 1) / 3 * 2 < *nitems_alloc)
memory_exhausted(_("integer overflow"));
@ -450,7 +455,7 @@ growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
*/
static void
eats(char const * name, lineno_t num, char const * rname, lineno_t rnum)
eats(char const *name, lineno_t num, char const *rname, lineno_t rnum)
{
filename = name;
linenum = num;
@ -459,7 +464,7 @@ eats(char const * name, lineno_t num, char const * rname, lineno_t rnum)
}
static void
eat(char const * name, lineno_t num)
eat(char const *name, lineno_t num)
{
eats(name, num, NULL, -1);
}
@ -472,10 +477,10 @@ verror(const char *string, va_list args)
* "*" -v on BSD systems.
*/
if (filename)
fprintf(stderr, _("\"%s\", line %" PRIdLINENO ": "), filename, linenum);
fprintf(stderr, _("\"%s\", line %d: "), filename, linenum);
vfprintf(stderr, string, args);
if (rfilename != NULL)
fprintf(stderr, _(" (rule from \"%s\", line %" PRIdLINENO ")"),
fprintf(stderr, _(" (rule from \"%s\", line %d)"),
rfilename, rlinenum);
fprintf(stderr, "\n");
}
@ -504,7 +509,7 @@ warning(const char *string,...)
}
static void
close_file(FILE *stream, char const * dir, 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);
@ -537,7 +542,7 @@ usage(FILE *stream, int status)
ancestors. After this is done, all files are accessed with names
relative to DIR. */
static void
change_directory(char const * dir)
change_directory(char const *dir)
{
if (chdir(dir) != 0)
{
@ -564,7 +569,7 @@ static const char *leapsec;
static const char *yitcommand;
int
main(int argc, char *argv[])
main(int argc, char **argv)
{
int c,
k;
@ -573,7 +578,7 @@ main(int argc, char *argv[])
#ifndef WIN32
umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
#endif /* !WIN32 */
#endif
progname = argv[0];
if (TYPE_BIT(zic_t) <64)
{
@ -632,7 +637,10 @@ main(int argc, char *argv[])
break;
case 'y':
if (yitcommand == NULL)
{
warning(_("-y is obsolescent"));
yitcommand = strdup(optarg);
}
else
{
fprintf(stderr,
@ -721,8 +729,8 @@ main(int argc, char *argv[])
}
static bool
componentcheck(char const * name, char const * component,
char const * component_end)
componentcheck(char const *name, char const *component,
char const *component_end)
{
enum
{
@ -813,7 +821,7 @@ namecheck(const char *name)
*/
#ifdef HAVE_SYMLINK
static char *
relname(char const * from, char const * to)
relname(char const *from, char const *to)
{
size_t i,
taillen,
@ -853,12 +861,12 @@ relname(char const * from, char const * to)
}
return result;
}
#endif /* HAVE_SYMLINK */
#endif /* HAVE_SYMLINK */
/* Hard link FROM to TO, following any symbolic links.
Return 0 if successful, an error number otherwise. */
static int
hardlinkerr(char const * from, char const * to)
hardlinkerr(char const *from, char const *to)
{
int r = linkat(AT_FDCWD, from, AT_FDCWD, to, AT_SYMLINK_FOLLOW);
@ -866,7 +874,7 @@ hardlinkerr(char const * from, char const * to)
}
static void
dolink(char const * fromfield, char const * tofield, bool staysymlink)
dolink(char const *fromfield, char const *tofield, bool staysymlink)
{
bool todirs_made = false;
int link_errno;
@ -921,7 +929,7 @@ dolink(char const * fromfield, char const * tofield, bool staysymlink)
strerror(link_errno));
}
else
#endif /* HAVE_SYMLINK */
#endif /* HAVE_SYMLINK */
{
FILE *fp,
*tp;
@ -1010,7 +1018,7 @@ static const zic_t early_time = (WORK_AROUND_GNOME_BUG_730332
/* Return true if NAME is a directory. */
static bool
itsdir(char const * name)
itsdir(char const *name)
{
struct stat st;
int res = stat(name, &st);
@ -1035,7 +1043,7 @@ itsdir(char const * name)
/* Return true if NAME is a symbolic link. */
static bool
itssymlink(char const * name)
itssymlink(char const *name)
{
#ifdef HAVE_SYMLINK
char c;
@ -1203,6 +1211,9 @@ infile(const char *name)
wantcont = inzcont(fields, nfields);
else
{
struct lookup const *line_codes
= name == leapsec ? leap_line_codes : zi_line_codes;
lp = byword(fields[0], line_codes);
if (lp == NULL)
error(_("input line of unknown type"));
@ -1221,12 +1232,7 @@ infile(const char *name)
wantcont = false;
break;
case LC_LEAP:
if (name != leapsec)
warning(_("%s: Leap line in non leap"
" seconds file %s"),
progname, name);
else
inleap(fields, nfields);
inleap(fields, nfields);
wantcont = false;
break;
default: /* "cannot happen" */
@ -1251,7 +1257,7 @@ infile(const char *name)
* Call error with errstring and return zero on errors.
*/
static zic_t
gethms(char const * string, char const * errstring, bool signable)
gethms(char const *string, char const *errstring, bool signable)
{
/* PG: make hh be int not zic_t to avoid sscanf portability issues */
int hh;
@ -1360,7 +1366,7 @@ inzone(char **fields, int nfields)
strcmp(zones[i].z_name, fields[ZF_NAME]) == 0)
{
error(_("duplicate zone name %s"
" (file \"%s\", line %" PRIdLINENO ")"),
" (file \"%s\", line %d)"),
fields[ZF_NAME],
zones[i].z_filename,
zones[i].z_linenum);
@ -1574,21 +1580,11 @@ inleap(char **fields, int nfields)
positive = false;
count = 1;
}
else if (strcmp(cp, "--") == 0)
{
positive = false;
count = 2;
}
else if (strcmp(cp, "+") == 0)
{
positive = true;
count = 1;
}
else if (strcmp(cp, "++") == 0)
{
positive = true;
count = 2;
}
else
{
error(_("illegal CORRECTION field on Leap line"));
@ -1600,9 +1596,9 @@ inleap(char **fields, int nfields)
return;
}
t = tadd(t, tod);
if (t < early_time)
if (t < 0)
{
error(_("leap second precedes Big Bang"));
error(_("leap second precedes Epoch"));
return;
}
leapadd(t, positive, lp->l_value, count);
@ -1635,7 +1631,7 @@ inlink(char **fields, int nfields)
}
static void
rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
const char *typep, const char *monthp, const char *dayp,
const char *timep)
{
@ -1754,11 +1750,14 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
error(_("typed single year"));
return;
}
warning(_("year type \"%s\" is obsolete; use \"-\" instead"),
typep);
rp->r_yrtype = ecpyalloc(typep);
}
/*
* Day work. Accept things such as: 1 last-Sunday Sun<=20 Sun>=7
* Day work. Accept things such as: 1 lastSunday last-Sunday
* (undocumented; warn about this) Sun<=20 Sun>=7
*/
dp = ecpyalloc(dayp);
if ((lp = byword(dp, lasts)) != NULL)
@ -2141,7 +2140,7 @@ writezone(const char *const name, const char *const string, char version)
writetype[type] = true;
}
}
#endif /* !defined
#endif /* !defined
* LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
thistypecnt = 0;
for (i = 0; i < typecnt; ++i)
@ -2324,7 +2323,7 @@ abbroffset(char *buf, zic_t offset)
}
static size_t
doabbr(char *abbr, struct zone const * zp, char const * letters,
doabbr(char *abbr, struct zone const *zp, char const *letters,
zic_t stdoff, bool doquotes)
{
char *cp;
@ -2410,7 +2409,7 @@ stringoffset(char *result, zic_t offset)
}
static int
stringrule(char *result, const struct rule * const rp, const zic_t dstoff,
stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
const zic_t gmtoff)
{
zic_t tod = rp->r_tod;
@ -2492,7 +2491,7 @@ stringrule(char *result, const struct rule * const rp, const zic_t dstoff,
}
static int
rule_cmp(struct rule const * a, struct rule const * b)
rule_cmp(struct rule const *a, struct rule const *b)
{
if (!a)
return -!!b;
@ -2510,7 +2509,7 @@ enum
YEAR_BY_YEAR_ZONE = 1};
static int
stringzone(char *result, struct zone const * zpfirst, ptrdiff_t zonecount)
stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
{
const struct zone *zp;
struct rule *rp;
@ -2645,7 +2644,7 @@ stringzone(char *result, struct zone const * zpfirst, ptrdiff_t zonecount)
}
static void
outzone(const struct zone * zpfirst, ptrdiff_t zonecount)
outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
{
const struct zone *zp;
struct rule *rp;
@ -2851,9 +2850,10 @@ outzone(const struct zone * zpfirst, ptrdiff_t zonecount)
{
ptrdiff_t k;
zic_t jtime,
ktime = 0;
ktime;
zic_t offset;
INITIALIZE(ktime);
if (useuntil)
{
/*
@ -2930,7 +2930,8 @@ outzone(const struct zone * zpfirst, ptrdiff_t zonecount)
continue;
}
if (*startbuf == '\0' &&
startoff == oadd(zp->z_gmtoff, stdoff))
startoff == oadd(zp->z_gmtoff,
stdoff))
{
doabbr(startbuf,
zp,
@ -3046,7 +3047,7 @@ addtt(zic_t starttime, int type)
}
static int
addtype(zic_t gmtoff, char const * abbr, bool isdst, bool ttisstd, bool ttisgmt)
addtype(zic_t gmtoff, char const *abbr, bool isdst, bool ttisstd, bool ttisgmt)
{
int i,
j;
@ -3105,14 +3106,7 @@ leapadd(zic_t t, bool positive, int rolling, int count)
}
for (i = 0; i < leapcnt; ++i)
if (t <= trans[i])
{
if (t == trans[i])
{
error(_("repeated leap second moment"));
exit(EXIT_FAILURE);
}
break;
}
do
{
for (j = leapcnt; j > i; --j)
@ -3133,19 +3127,26 @@ adjleap(void)
{
int i;
zic_t last = 0;
zic_t prevtrans = 0;
/*
* propagate leap seconds forward
*/
for (i = 0; i < leapcnt; ++i)
{
if (trans[i] - prevtrans < 28 * SECSPERDAY)
{
error(_("Leap seconds too close together"));
exit(EXIT_FAILURE);
}
prevtrans = trans[i];
trans[i] = tadd(trans[i], last);
last = corr[i] += last;
}
}
static char *
shellquote(char *b, char const * s)
shellquote(char *b, char const *s)
{
*b++ = '\'';
while (*s)
@ -3192,7 +3193,7 @@ yearistype(zic_t year, const char *type)
exit(EXIT_FAILURE);
}
/* Is A a space character in the C locale? */
/* Is A a space character in the C locale? */
static bool
is_space(char a)
{
@ -3363,8 +3364,21 @@ itsabbr(const char *abbr, const char *word)
return true;
}
/* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case. */
static bool
ciprefix(char const *abbr, char const *word)
{
do
if (!*abbr)
return true;
while (lowerit(*abbr++) == lowerit(*word++));
return false;
}
static const struct lookup *
byword(const char *word, const struct lookup * table)
byword(const char *word, const struct lookup *table)
{
const struct lookup *foundlp;
const struct lookup *lp;
@ -3372,6 +3386,23 @@ byword(const char *word, const struct lookup * table)
if (word == NULL || table == NULL)
return NULL;
/*
* If TABLE is LASTS and the word starts with "last" followed by a
* non-'-', skip the "last" and look in WDAY_NAMES instead. Warn about any
* usage of the undocumented prefix "last-".
*/
if (table == lasts && ciprefix("last", word) && word[4])
{
if (word[4] == '-')
warning(_("\"%s\" is undocumented; use \"last%s\" instead"),
word, word + 5);
else
{
word += 4;
table = wday_names;
}
}
/*
* Look for exact match.
*/
@ -3384,13 +3415,31 @@ byword(const char *word, const struct lookup * table)
*/
foundlp = NULL;
for (lp = table; lp->l_word != NULL; ++lp)
if (itsabbr(word, lp->l_word))
if (ciprefix(word, lp->l_word))
{
if (foundlp == NULL)
foundlp = lp;
else
return NULL; /* multiple inexact matches */
}
/* Warn about any backward-compatibility issue with pre-2017c zic. */
if (foundlp)
{
bool pre_2017c_match = false;
for (lp = table; lp->l_word; lp++)
if (itsabbr(word, lp->l_word))
{
if (pre_2017c_match)
{
warning(_("\"%s\" is ambiguous in pre-2017c zic"), word);
break;
}
pre_2017c_match = true;
}
}
return foundlp;
}
@ -3479,7 +3528,7 @@ tadd(zic_t t1, zic_t t2)
*/
static zic_t
rpytime(const struct rule * rp, zic_t wantedy)
rpytime(const struct rule *rp, zic_t wantedy)
{
int m,
i;
@ -3571,7 +3620,7 @@ will not work with pre-2004 versions of zic"));
return min_time;
if (dayoff > max_time / SECSPERDAY)
return max_time;
t = (zic_t) dayoff *SECSPERDAY;
t = (zic_t) dayoff * SECSPERDAY;
return tadd(t, rp->r_tod);
}
@ -3615,18 +3664,22 @@ newabbr(const char *string)
do it for ARGNAME too. Exit with failure if there is trouble.
Do not consider an existing non-directory to be trouble. */
static void
mkdirs(char const * argname, bool ancestors)
mkdirs(char const *argname, bool ancestors)
{
char *name;
char *cp;
cp = name = ecpyalloc(argname);
/*
* On MS-Windows systems, do not worry about drive letters or backslashes,
* as this should suffice in practice. Time zone names do not use drive
* letters and backslashes. If the -d option of zic does not name an
* already-existing directory, it can use slashes to separate the
* already-existing ancestor prefix from the to-be-created subdirectories.
*/
/* 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++;