diff options
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) { |