diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2018-10-19 19:36:34 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2018-10-19 19:36:34 -0400 |
commit | 12bfb778ce688fc662a6cb35f6298734fcf4856f (patch) | |
tree | 63cc6cc5290ec5f8a5435c721632304a90a29436 /src/timezone/localtime.c | |
parent | 13877d30f2ec93f6043937f76af207dcc614a4e7 (diff) | |
download | postgresql-12bfb778ce688fc662a6cb35f6298734fcf4856f.tar.gz postgresql-12bfb778ce688fc662a6cb35f6298734fcf4856f.zip |
Sync our copy of the timezone library with IANA release tzcode2018f.
About half of this is purely cosmetic changes to reduce the diff between
our code and theirs, like inserting "const" markers where they have them.
The other half is tracking actual code changes in zic.c and localtime.c.
I don't think any of these represent near-term compatibility hazards, but
it seems best to stay up to date.
I also fixed longstanding bugs in our code for producing the
known_abbrevs.txt list, which by chance hadn't been exposed before,
but which resulted in some garbage output after applying the upstream
changes in zic.c. Notably, because upstream removed their old phony
transitions at the Big Bang, it's now necessary to cope with TZif files
containing no DST transition times at all.
Diffstat (limited to 'src/timezone/localtime.c')
-rw-r--r-- | src/timezone/localtime.c | 126 |
1 files changed, 87 insertions, 39 deletions
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index e3029d841d7..96e62aff3b4 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -1,3 +1,5 @@ +/* Convert timestamp from pg_time_t to struct pg_tm. */ + /* * This file is in the public domain, so clarified as of * 1996-06-05 by Arthur David Olson. @@ -117,7 +119,7 @@ init_ttinfo(struct ttinfo *s, int32 gmtoff, bool isdst, int abbrind) } static int32 -detzcode(const char *codep) +detzcode(const char *const codep) { int32 result; int i; @@ -143,7 +145,7 @@ detzcode(const char *codep) } static int64 -detzcode64(const char *codep) +detzcode64(const char *const codep) { uint64 result; int i; @@ -258,8 +260,13 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend, int32 charcnt = detzcode(up->tzhead.tzh_charcnt); char const *p = up->buf + tzheadsize; + /* + * Although tzfile(5) currently requires typecnt to be nonzero, + * support future formats that may allow zero typecnt in files that + * have a TZ string and no transitions. + */ if (!(0 <= leapcnt && leapcnt < TZ_MAX_LEAPS - && 0 < typecnt && typecnt < TZ_MAX_TYPES + && 0 <= typecnt && typecnt < TZ_MAX_TYPES && 0 <= timecnt && timecnt < TZ_MAX_TIMES && 0 <= charcnt && charcnt < TZ_MAX_CHARS && (ttisstdcnt == typecnt || ttisstdcnt == 0) @@ -416,8 +423,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend, struct state *ts = &lsp->u.st; up->buf[nread - 1] = '\0'; - if (tzparse(&up->buf[1], ts, false) - && ts->typecnt == 2) + if (tzparse(&up->buf[1], ts, false)) { /* * Attempt to reuse existing abbreviations. Without this, @@ -430,7 +436,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend, int gotabbr = 0; int charcnt = sp->charcnt; - for (i = 0; i < 2; i++) + for (i = 0; i < ts->typecnt; i++) { char *tsabbr = ts->chars + ts->ttis[i].tt_abbrind; int j; @@ -455,7 +461,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend, } } } - if (gotabbr == 2) + if (gotabbr == ts->typecnt) { sp->charcnt = charcnt; @@ -470,7 +476,8 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend, sp->timecnt--; for (i = 0; i < ts->timecnt; i++) - if (sp->ats[sp->timecnt - 1] < ts->ats[i]) + if (sp->timecnt == 0 + || sp->ats[sp->timecnt - 1] < ts->ats[i]) break; while (i < ts->timecnt && sp->timecnt < TZ_MAX_TIMES) @@ -481,11 +488,13 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend, sp->timecnt++; i++; } - sp->ttis[sp->typecnt++] = ts->ttis[0]; - sp->ttis[sp->typecnt++] = ts->ttis[1]; + for (i = 0; i < ts->typecnt; i++) + sp->ttis[sp->typecnt++] = ts->ttis[i]; } } } + if (sp->typecnt == 0) + return EINVAL; if (sp->timecnt > 1) { for (i = 1; i < sp->timecnt; ++i) @@ -507,6 +516,18 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend, } /* + * Infer sp->defaulttype from the data. Although this default type is + * always zero for data from recent tzdb releases, things are trickier for + * data from tzdb 2018e or earlier. + * + * The first set of heuristics work around bugs in 32-bit data generated + * by tzdb 2013c or earlier. The workaround is for zones like + * Australia/Macquarie where timestamps before the first transition have a + * time type that is not the earliest standard-time type. See: + * https://mm.icann.org/pipermail/tz/2013-May/019368.html + */ + + /* * If type 0 is unused in transitions, it's the type to use for early * times. */ @@ -529,6 +550,11 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend, } /* + * The next heuristics are for data generated by tzdb 2018e or earlier, + * for zones like EST5EDT where the first transition is to DST. + */ + + /* * If no result yet, find the first standard type. If there is none, punt * to type zero. */ @@ -542,7 +568,14 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend, break; } } + + /* + * A simple 'sp->defaulttype = 0;' would suffice here if we didn't have to + * worry about 2018e-or-earlier data. Even simpler would be to remove the + * defaulttype member and just use 0 in its place. + */ sp->defaulttype = i; + return 0; } @@ -601,10 +634,11 @@ static const int year_lengths[2] = { }; /* - * Given a pointer into a time zone string, scan until a character that is not - * a valid character in a zone name is found. Return a pointer to that - * character. + * Given a pointer into a timezone string, scan until a character that is not + * a valid character in a time zone abbreviation is found. + * Return a pointer to that character. */ + static const char * getzname(const char *strp) { @@ -617,15 +651,17 @@ getzname(const char *strp) } /* - * Given a pointer into an extended time zone string, scan until the ending - * delimiter of the zone name is located. Return a pointer to the delimiter. + * Given a pointer into an extended timezone string, scan until the ending + * delimiter of the time zone abbreviation is located. + * Return a pointer to the delimiter. * * As with getzname above, the legal character set is actually quite * restricted, with other characters producing undefined results. * We don't do any checking here; checking is done later in common-case code. */ + static const char * -getqzname(const char *strp, int delim) +getqzname(const char *strp, const int delim) { int c; @@ -635,13 +671,14 @@ getqzname(const char *strp, int delim) } /* - * Given a pointer into a time zone string, extract a number from that string. + * Given a pointer into a timezone string, extract a number from that string. * Check that the number is within a specified range; if it is not, return * NULL. * Otherwise, return a pointer to the first character not part of the number. */ + static const char * -getnum(const char *strp, int *nump, int min, int max) +getnum(const char *strp, int *const nump, const int min, const int max) { char c; int num; @@ -663,14 +700,15 @@ getnum(const char *strp, int *nump, int min, int max) } /* - * Given a pointer into a time zone string, extract a number of seconds, + * Given a pointer into a timezone string, extract a number of seconds, * in hh[:mm[:ss]] form, from the string. * If any error occurs, return NULL. * Otherwise, return a pointer to the first character not part of the number * of seconds. */ + static const char * -getsecs(const char *strp, int32 *secsp) +getsecs(const char *strp, int32 *const secsp) { int num; @@ -704,13 +742,14 @@ getsecs(const char *strp, int32 *secsp) } /* - * Given a pointer into a time zone string, extract an offset, in + * Given a pointer into a timezone string, extract an offset, in * [+-]hh[:mm[:ss]] form, from the string. * If any error occurs, return NULL. * Otherwise, return a pointer to the first character not part of the time. */ + static const char * -getoffset(const char *strp, int32 *offsetp) +getoffset(const char *strp, int32 *const offsetp) { bool neg = false; @@ -730,13 +769,14 @@ getoffset(const char *strp, int32 *offsetp) } /* - * Given a pointer into a time zone string, extract a rule in the form + * Given a pointer into a timezone string, extract a rule in the form * date[/time]. See POSIX section 8 for the format of "date" and "time". * If a valid rule is not found, return NULL. * Otherwise, return a pointer to the first character not part of the rule. */ + static const char * -getrule(const char *strp, struct rule *rulep) +getrule(const char *strp, struct rule *const rulep) { if (*strp == 'J') { @@ -795,9 +835,10 @@ getrule(const char *strp, struct rule *rulep) * Given a year, a rule, and the offset from UT at the time that rule takes * effect, calculate the year-relative time that rule takes effect. */ + static int32 -transtime(int year, const struct rule *rulep, - int32 offset) +transtime(const int year, const struct rule *const rulep, + const int32 offset) { bool leapyear; int32 value; @@ -967,7 +1008,7 @@ tzparse(const char *name, struct state *sp, bool lastditch) { dstname = name; name = getzname(name); - dstlen = name - dstname; /* length of DST zone name */ + dstlen = name - dstname; /* length of DST abbr. */ } if (!dstlen) return false; @@ -1039,8 +1080,8 @@ tzparse(const char *name, struct state *sp, bool lastditch) /* * Two transitions per year, from EPOCH_YEAR forward. */ - init_ttinfo(&sp->ttis[0], -dstoffset, true, stdlen + 1); - init_ttinfo(&sp->ttis[1], -stdoffset, false, 0); + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1); sp->defaulttype = 0; timecnt = 0; janfirst = 0; @@ -1089,19 +1130,15 @@ tzparse(const char *name, struct state *sp, bool lastditch) if (!increment_overflow_time (&sp->ats[timecnt], janoffset + starttime)) - sp->types[timecnt++] = reversed; - else if (janoffset) - sp->defaulttype = reversed; + sp->types[timecnt++] = !reversed; sp->ats[timecnt] = janfirst; if (!increment_overflow_time (&sp->ats[timecnt], janoffset + endtime)) { - sp->types[timecnt++] = !reversed; + sp->types[timecnt++] = reversed; yearlim = year + YEARSPERREPEAT + 1; } - else if (janoffset) - sp->defaulttype = !reversed; } if (increment_overflow_time (&janfirst, janoffset + yearsecs)) @@ -1110,7 +1147,10 @@ tzparse(const char *name, struct state *sp, bool lastditch) } sp->timecnt = timecnt; if (!timecnt) + { + sp->ttis[0] = sp->ttis[1]; sp->typecnt = 1; /* Perpetual DST. */ + } else if (YEARSPERREPEAT < year - yearbeg) sp->goback = sp->goahead = true; } @@ -1179,7 +1219,6 @@ tzparse(const char *name, struct state *sp, bool lastditch) * otherwise, add the standard time offset to the * transition time. */ - /* * Transitions from DST to DDST will effectively disappear * since POSIX provides for only one DST offset. @@ -1233,7 +1272,7 @@ tzparse(const char *name, struct state *sp, bool lastditch) } static void -gmtload(struct state *sp) +gmtload(struct state *const sp) { if (tzload(gmt, NULL, sp, true) != 0) tzparse(gmt, sp, true); @@ -1248,7 +1287,7 @@ gmtload(struct state *sp) */ static struct pg_tm * localsub(struct state const *sp, pg_time_t const *timep, - struct pg_tm *tmp) + struct pg_tm *const tmp) { const struct ttinfo *ttisp; int i; @@ -1316,6 +1355,11 @@ localsub(struct state const *sp, pg_time_t const *timep, } ttisp = &sp->ttis[i]; + /* + * To get (wrong) behavior that's compatible with System V Release 2.0 + * you'd replace the statement below with t += ttisp->tt_gmtoff; + * timesub(&t, 0L, sp, tmp); + */ result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); if (result) { @@ -1338,8 +1382,10 @@ pg_localtime(const pg_time_t *timep, const pg_tz *tz) * * Except we have a private "struct state" for GMT, so no sp is passed in. */ + static struct pg_tm * -gmtsub(pg_time_t const *timep, int32 offset, struct pg_tm *tmp) +gmtsub(pg_time_t const *timep, int32 offset, + struct pg_tm *tmp) { struct pg_tm *result; @@ -1354,6 +1400,7 @@ gmtsub(pg_time_t const *timep, int32 offset, struct pg_tm *tmp) return NULL; /* errno should be set by malloc */ gmtload(gmtptr); } + result = timesub(timep, offset, gmtptr, tmp); /* @@ -1378,6 +1425,7 @@ pg_gmtime(const pg_time_t *timep) * Return the number of leap years through the end of the given year * where, to make the math easy, the answer for year zero is defined as zero. */ + static int leaps_thru_end_of_nonneg(int y) { |