The client code mishandled timestamps after the last explicit transition in a TZif file that had both leap seconds and daylight saving time transitions projected into the indefinite future. When interpreting the TZ string embedded at the end of the TZif file, the code neglected to adjust the corresponding DST transitions for leap seconds, which meant the DST transitions were calculated to occur too early. For example, with TZ set to '.../zoneinfo-leaps/America/Los_Angeles', zdump reported a DST transition on 2038-03-14 from 01:59:32.999... to 02:59:33 instead of the correct transition from 01:59:59.999... to 03:00:00. * NEWS: Mention this. * localtime.c (tzloadbody): Apply leap second corrections to transitions derived from the trailing TZ string in a TZif file. (leapcorr) [!STD_INSPIRED]: Define in this case too, since tzloadbody now uses it. --- NEWS | 8 ++++++++ localtime.c | 35 +++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/NEWS b/NEWS index 961aa08..72e5c04 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,14 @@ Unreleased, experimental changes Changes to code + localtime.c no longer mishandles timestamps after the last + transition in a TZif file with leap seconds and with daylight + saving time transitions projected into the indefinite future. + For example, with TZ='America/Los_Angeles' with leap seconds, + zdump formerly reported a DST transition on 2038-03-14 + from 01:59:32.999... to 02:59:33 instead of the correct transition + from 01:59:59.999... to 03:00:00. + The configuration macros HAVE_TZNAME and USG_COMPAT should now be set to 1 if the system library supports the feature, and 2 if not. As before, these macros are nonzero if tzcode should support the diff --git a/localtime.c b/localtime.c index d3f8fc7..6623eac 100644 --- a/localtime.c +++ b/localtime.c @@ -158,6 +158,7 @@ static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t, struct tm *); static bool increment_overflow(int *, int); static bool increment_overflow_time(time_t *, int_fast32_t); +static int_fast64_t leapcorr(struct state const *, time_t); static bool normalize_overflow32(int_fast32_t *, int *, int); static struct tm *timesub(time_t const *, int_fast32_t, struct state const *, struct tm *); @@ -641,11 +642,13 @@ tzloadbody(char const *name, struct state *sp, bool doextend, for (i = 0; i < ts->timecnt; i++) if (sp->timecnt == 0 - || sp->ats[sp->timecnt - 1] < ts->ats[i]) + || (sp->ats[sp->timecnt - 1] + < ts->ats[i] + leapcorr(sp, ts->ats[i]))) break; while (i < ts->timecnt && sp->timecnt < TZ_MAX_TIMES) { - sp->ats[sp->timecnt] = ts->ats[i]; + sp->ats[sp->timecnt] + = ts->ats[i] + leapcorr(sp, ts->ats[i]); sp->types[sp->timecnt] = (sp->typecnt + ts->types[i]); sp->timecnt++; @@ -2239,20 +2242,6 @@ timeoff(struct tm *tmp, long offset) #endif /* defined STD_INSPIRED */ -/* -** XXX--is the below the right way to conditionalize?? -*/ - -#ifdef STD_INSPIRED - -/* -** IEEE Std 1003.1 (POSIX) says that 536457599 -** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which -** is not the case if we are accounting for leap seconds. -** So, we provide the following conversion routines for use -** when exchanging timestamps with POSIX conforming systems. -*/ - static int_fast64_t leapcorr(struct state const *sp, time_t t) { @@ -2268,6 +2257,20 @@ leapcorr(struct state const *sp, time_t t) return 0; } +/* +** XXX--is the below the right way to conditionalize?? +*/ + +#ifdef STD_INSPIRED + +/* +** IEEE Std 1003.1 (POSIX) says that 536457599 +** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which +** is not the case if we are accounting for leap seconds. +** So, we provide the following conversion routines for use +** when exchanging timestamps with POSIX conforming systems. +*/ + NETBSD_INSPIRED_EXTERN time_t time2posix_z(struct state *sp, time_t t) { -- 2.24.1