diff options
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; |