diff options
Diffstat (limited to 'src/timezone/localtime.c')
-rw-r--r-- | src/timezone/localtime.c | 103 |
1 files changed, 81 insertions, 22 deletions
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index a14215d6bd..a2faa82ac8 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -17,8 +17,9 @@ #include <fcntl.h> #include "datatype/timestamp.h" -#include "private.h" #include "pgtz.h" + +#include "private.h" #include "tzfile.h" @@ -54,6 +55,13 @@ 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. + * tzdefrules_loaded is 0 if not tried yet, +1 if good, -1 if failed. + */ +static struct state tzdefrules_s; +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. * POSIX 1003.1 section 8.1.1 says that the default DST rules are @@ -414,10 +422,10 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, { /* * Attempt to reuse existing abbreviations. Without this, - * America/Anchorage would stop working after 2037 when - * TZ_MAX_CHARS is 50, as sp->charcnt equals 42 (for LMT CAT CAWT - * CAPT AHST AHDT YST AKDT AKST) and ts->charcnt equals 10 (for - * AKST AKDT). Reusing means sp->charcnt can stay 42 in this + * America/Anchorage would be right on the edge after 2037 when + * TZ_MAX_CHARS is 50, as sp->charcnt equals 40 (for LMT AST AWT + * APT AHST AHDT YST AKDT AKST) and ts->charcnt equals 10 (for + * AKST AKDT). Reusing means sp->charcnt can stay 40 in this * example. */ int gotabbr = 0; @@ -451,6 +459,17 @@ tzloadbody(char const * name, char *canonname, struct state * sp, bool doextend, if (gotabbr == 2) { sp->charcnt = charcnt; + + /* + * Ignore any trailing, no-op transitions generated by zic as + * they don't help here and can run afoul of bugs in zic 2016j + * or earlier. + */ + while (1 < sp->timecnt + && (sp->types[sp->timecnt - 1] + == sp->types[sp->timecnt - 2])) + sp->timecnt--; + for (i = 0; i < ts->timecnt; i++) if (sp->ats[sp->timecnt - 1] < ts->ats[i]) break; @@ -489,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) @@ -930,7 +949,21 @@ tzparse(const char *name, struct state * sp, bool lastditch) charcnt = stdlen + 1; if (sizeof sp->chars < charcnt) return false; - load_ok = tzload(TZDEFRULES, NULL, sp, false) == 0; + + /* + * This bit also differs from the IANA code, which doesn't make any + * attempt to avoid repetitive loadings of the TZDEFRULES zone. + */ + if (tzdefrules_loaded == 0) + { + if (tzload(TZDEFRULES, NULL, &tzdefrules_s, false) == 0) + tzdefrules_loaded = 1; + else + tzdefrules_loaded = -1; + } + load_ok = (tzdefrules_loaded > 0); + if (load_ok) + memcpy(sp, &tzdefrules_s, sizeof(struct state)); } if (!load_ok) sp->leapcnt = 0; /* so, we're off a little */ @@ -974,6 +1007,8 @@ tzparse(const char *name, struct state * sp, bool lastditch) int yearlim; int timecnt; pg_time_t janfirst; + int32 janoffset = 0; + int yearbeg; ++name; if ((name = getrule(name, &start)) == NULL) @@ -994,8 +1029,23 @@ tzparse(const char *name, struct state * sp, bool lastditch) sp->defaulttype = 0; timecnt = 0; janfirst = 0; - yearlim = EPOCH_YEAR + YEARSPERREPEAT; - for (year = EPOCH_YEAR; year < yearlim; year++) + yearbeg = EPOCH_YEAR; + + do + { + int32 yearsecs + = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY; + + yearbeg--; + if (increment_overflow_time(&janfirst, -yearsecs)) + { + janoffset = -yearsecs; + break; + } + } while (EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg); + + yearlim = yearbeg + YEARSPERREPEAT + 1; + for (year = yearbeg; year < yearlim; year++) { int32 starttime = transtime(year, &start, stdoffset), @@ -1020,24 +1070,34 @@ tzparse(const char *name, struct state * sp, bool lastditch) { if (TZ_MAX_TIMES - 2 < timecnt) break; - yearlim = year + YEARSPERREPEAT + 1; sp->ats[timecnt] = janfirst; - if (increment_overflow_time - (&sp->ats[timecnt], starttime)) - break; - sp->types[timecnt++] = reversed; + if (!increment_overflow_time + (&sp->ats[timecnt], + janoffset + starttime)) + sp->types[timecnt++] = reversed; + else if (janoffset) + sp->defaulttype = reversed; sp->ats[timecnt] = janfirst; - if (increment_overflow_time - (&sp->ats[timecnt], endtime)) - break; - sp->types[timecnt++] = !reversed; + if (!increment_overflow_time + (&sp->ats[timecnt], + janoffset + endtime)) + { + sp->types[timecnt++] = !reversed; + yearlim = year + YEARSPERREPEAT + 1; + } + else if (janoffset) + sp->defaulttype = !reversed; } - if (increment_overflow_time(&janfirst, yearsecs)) + if (increment_overflow_time + (&janfirst, janoffset + yearsecs)) break; + janoffset = 0; } sp->timecnt = timecnt; if (!timecnt) sp->typecnt = 1; /* Perpetual DST. */ + else if (YEARSPERREPEAT < year - yearbeg) + sp->goback = sp->goahead = true; } else { @@ -1280,9 +1340,8 @@ gmtsub(pg_time_t const * timep, int32 offset, struct pg_tm * tmp) result = timesub(timep, offset, gmtptr, tmp); /* - * Could get fancy here and deliver something such as "UT+xxxx" or - * "UT-xxxx" if offset is non-zero, but this is no time for a treasure - * hunt. + * Could get fancy here and deliver something such as "+xx" or "-xx" if + * offset is non-zero, but this is no time for a treasure hunt. */ if (offset != 0) tmp->tm_zone = wildabbr; |