diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2017-09-22 00:04:21 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2017-09-22 00:04:21 -0400 |
commit | e25f4401dad7829463efff1f4655f2c6d6b076ff (patch) | |
tree | 92e5cc63e00961b31abe4eedfb23bc3f583a6c73 /src/timezone/localtime.c | |
parent | ea31541f564851611fee58fe2019c866ce0e8aa0 (diff) |
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
Diffstat (limited to 'src/timezone/localtime.c')
-rw-r--r-- | src/timezone/localtime.c | 138 |
1 files changed, 66 insertions, 72 deletions
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index 04105b2c968..d946e882aa6 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -26,15 +26,15 @@ #ifndef WILDABBR /* * Someone might make incorrect use of a time zone abbreviation: - * 1. They might reference tzname[0] before calling tzset (explicitly + * 1. They might reference tzname[0] before calling tzset (explicitly * or implicitly). - * 2. They might reference tzname[1] before calling tzset (explicitly + * 2. They might reference tzname[1] before calling tzset (explicitly * or implicitly). - * 3. They might reference tzname[1] after setting to a time zone + * 3. They might reference tzname[1] after setting to a time zone * in which Daylight Saving Time is never observed. - * 4. They might reference tzname[0] after setting to a time zone + * 4. They might reference tzname[0] after setting to a time zone * in which Standard Time is never observed. - * 5. They might reference tm.TM_ZONE after calling offtime. + * 5. They might reference tm.TM_ZONE after calling offtime. * What's best to do in the above cases is open to debate; * for now, we just set things up so that in any of the five cases * WILDABBR is used. Another possibility: initialize tzname[0] to the @@ -44,18 +44,14 @@ * that tzname[0] has the "normal" length of three characters). */ #define WILDABBR " " -#endif /* !defined WILDABBR */ +#endif /* !defined WILDABBR */ static const char wildabbr[] = WILDABBR; static const char gmt[] = "GMT"; -/* The minimum and maximum finite time values. This assumes no padding. */ -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. + * PG: 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; @@ -63,12 +59,12 @@ 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. + * 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. */ -#define TZDEFRULESTRING ",M4.1.0,M10.5.0" +#define TZDEFRULESTRING ",M3.2.0,M11.1.0" /* structs ttinfo, lsinfo, state have been moved to pgtz.h */ @@ -112,7 +108,7 @@ static struct pg_tm tm; /* Initialize *S to a value based on GMTOFF, ISDST, and ABBRIND. */ static void -init_ttinfo(struct ttinfo * s, int32 gmtoff, bool isdst, int abbrind) +init_ttinfo(struct ttinfo *s, int32 gmtoff, bool isdst, int abbrind) { s->tt_gmtoff = gmtoff; s->tt_isdst = isdst; @@ -189,16 +185,14 @@ union input_buffer /* The entire buffer. */ char buf[2 * sizeof(struct tzhead) + 2 * sizeof(struct state) - + 4 * TZ_MAX_TIMES]; + + 4 * TZ_MAX_TIMES]; }; /* Local storage needed for 'tzloadbody'. */ union local_storage { - /* We don't need the "fullname" member */ - /* The results of analyzing the file's contents after it is opened. */ - struct + struct file_analysis { /* The input buffer. */ union input_buffer u; @@ -206,6 +200,8 @@ union local_storage /* A temporary state used for parsing a TZ string in the file. */ struct state st; } u; + + /* We don't need the "fullname" member */ }; /* Load tz data from the file named NAME into *SP. Read extended @@ -215,8 +211,8 @@ union local_storage * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!). */ static int -tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, - union local_storage * lsp) +tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend, + union local_storage *lsp) { int i; int fid; @@ -255,6 +251,8 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, { int32 ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt); int32 ttisgmtcnt = detzcode(up->tzhead.tzh_ttisgmtcnt); + int64 prevtr = 0; + int32 prevcorr = 0; int32 leapcnt = detzcode(up->tzhead.tzh_leapcnt); int32 timecnt = detzcode(up->tzhead.tzh_timecnt); int32 typecnt = detzcode(up->tzhead.tzh_typecnt); @@ -270,7 +268,7 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, return EINVAL; if (nread < (tzheadsize /* struct tzhead */ - + timecnt * stored /* ats */ + + timecnt * stored /* ats */ + timecnt /* types */ + typecnt * 6 /* ttinfos */ + charcnt /* chars */ @@ -285,8 +283,8 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, /* * Read transitions, discarding those out of pg_time_t range. But - * pretend the last transition before time_t_min occurred at - * time_t_min. + * pretend the last transition before TIME_T_MIN occurred at + * TIME_T_MIN. */ timecnt = 0; for (i = 0; i < sp->timecnt; ++i) @@ -294,12 +292,12 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, int64 at = stored == 4 ? detzcode(p) : detzcode64(p); - sp->types[i] = at <= time_t_max; + sp->types[i] = at <= TIME_T_MAX; if (sp->types[i]) { pg_time_t attime - = ((TYPE_SIGNED(pg_time_t) ? at < time_t_min : at < 0) - ? time_t_min : at); + = ((TYPE_SIGNED(pg_time_t) ? at < TIME_T_MIN : at < 0) + ? TIME_T_MIN : at); if (timecnt && attime <= sp->ats[timecnt - 1]) { @@ -354,20 +352,22 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, int32 corr = detzcode(p + stored); p += stored + 4; - if (tr <= time_t_max) + /* Leap seconds cannot occur before the Epoch. */ + if (tr < 0) + return EINVAL; + if (tr <= TIME_T_MAX) { - pg_time_t trans - = ((TYPE_SIGNED(pg_time_t) ? tr < time_t_min : tr < 0) - ? time_t_min : tr); - - if (leapcnt && trans <= sp->lsis[leapcnt - 1].ls_trans) - { - if (trans < sp->lsis[leapcnt - 1].ls_trans) - return EINVAL; - leapcnt--; - } - sp->lsis[leapcnt].ls_trans = trans; - sp->lsis[leapcnt].ls_corr = corr; + /* + * Leap seconds cannot occur more than once per UTC month, and + * UTC months are at least 28 days long (minus 1 second for a + * negative leap second). Each leap second's correction must + * differ from the previous one's by 1 second. + */ + if (tr - prevtr < 28 * SECSPERDAY - 1 + || (corr != prevcorr - 1 && corr != prevcorr + 1)) + return EINVAL; + sp->lsis[leapcnt].ls_trans = prevtr = tr; + sp->lsis[leapcnt].ls_corr = prevcorr = corr; leapcnt++; } } @@ -508,7 +508,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) @@ -553,7 +553,7 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!). */ int -tzload(const char *name, char *canonname, struct state * sp, bool doextend) +tzload(const char *name, char *canonname, struct state *sp, bool doextend) { union local_storage *lsp = malloc(sizeof *lsp); @@ -569,7 +569,7 @@ tzload(const char *name, char *canonname, struct state * sp, bool doextend) } static bool -typesequiv(const struct state * sp, int a, int b) +typesequiv(const struct state *sp, int a, int b) { bool result; @@ -737,7 +737,7 @@ getoffset(const char *strp, int32 *offsetp) * 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 *rulep) { if (*strp == 'J') { @@ -788,7 +788,7 @@ getrule(const char *strp, struct rule * rulep) strp = getoffset(strp, &rulep->r_time); } else - rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ + rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ return strp; } @@ -797,7 +797,7 @@ getrule(const char *strp, struct rule * rulep) * effect, calculate the year-relative time that rule takes effect. */ static int32 -transtime(int year, const struct rule * rulep, +transtime(int year, const struct rule *rulep, int32 offset) { bool leapyear; @@ -894,7 +894,7 @@ transtime(int year, const struct rule * rulep, * Returns true on success, false on failure. */ bool -tzparse(const char *name, struct state * sp, bool lastditch) +tzparse(const char *name, struct state *sp, bool lastditch) { const char *stdname; const char *dstname = NULL; @@ -921,7 +921,7 @@ tzparse(const char *name, struct state * sp, bool lastditch) stdlen = (sizeof sp->chars) - 1; charcnt = stdlen + 1; stdoffset = 0; - sp->goback = sp->goahead = false; /* simulate failed tzload() */ + sp->goback = sp->goahead = false; /* simulate failed tzload() */ load_ok = false; } else @@ -1217,7 +1217,7 @@ tzparse(const char *name, struct state * sp, bool lastditch) } static void -gmtload(struct state * sp) +gmtload(struct state *sp) { if (tzload(gmt, NULL, sp, true) != 0) tzparse(gmt, sp, true); @@ -1231,8 +1231,8 @@ gmtload(struct state * sp) * but it *is* desirable.) */ static struct pg_tm * -localsub(struct state const * sp, pg_time_t const * timep, - struct pg_tm * tmp) +localsub(struct state const *sp, pg_time_t const *timep, + struct pg_tm *tmp) { const struct ttinfo *ttisp; int i; @@ -1323,7 +1323,7 @@ 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; @@ -1362,15 +1362,22 @@ pg_gmtime(const pg_time_t *timep) * where, to make the math easy, the answer for year zero is defined as zero. */ static int +leaps_thru_end_of_nonneg(int y) +{ + return y / 4 - y / 100 + y / 400; +} + +static int leaps_thru_end_of(const int y) { - return (y >= 0) ? (y / 4 - y / 100 + y / 400) : - -(leaps_thru_end_of(-(y + 1)) + 1); + return (y < 0 + ? -1 - leaps_thru_end_of_nonneg(-1 - y) + : leaps_thru_end_of_nonneg(y)); } static struct pg_tm * timesub(const pg_time_t *timep, int32 offset, - const struct state * sp, struct pg_tm * tmp) + const struct state *sp, struct pg_tm *tmp) { const struct lsinfo *lp; pg_time_t tdays; @@ -1390,22 +1397,9 @@ timesub(const pg_time_t *timep, int32 offset, lp = &sp->lsis[i]; if (*timep >= lp->ls_trans) { - if (*timep == lp->ls_trans) - { - hit = ((i == 0 && lp->ls_corr > 0) || - lp->ls_corr > sp->lsis[i - 1].ls_corr); - if (hit) - while (i > 0 && - sp->lsis[i].ls_trans == - sp->lsis[i - 1].ls_trans + 1 && - sp->lsis[i].ls_corr == - sp->lsis[i - 1].ls_corr + 1) - { - ++hit; - --i; - } - } corr = lp->ls_corr; + hit = (*timep == lp->ls_trans + && (i == 0 ? 0 : lp[-1].ls_corr) < corr); break; } } @@ -1529,13 +1523,13 @@ increment_overflow_time(pg_time_t *tp, int32 j) { /*---------- * This is like - * 'if (! (time_t_min <= *tp + j && *tp + j <= time_t_max)) ...', + * 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...', * except that it does the right thing even if *tp + j would overflow. *---------- */ if (!(j < 0 - ? (TYPE_SIGNED(pg_time_t) ? time_t_min - j <= *tp : -1 - j < *tp) - : *tp <= time_t_max - j)) + ? (TYPE_SIGNED(pg_time_t) ? TIME_T_MIN - j <= *tp : -1 - j < *tp) + : *tp <= TIME_T_MAX - j)) return true; *tp += j; return false; |