[tz] Fiji change exposed Y2038 bug in localtime.c

Paul Eggert eggert at cs.ucla.edu
Thu Oct 1 09:30:43 UTC 2015

The recent change to the Pacific/Fiji data added a transition from +1300 to 
+1200 at 2018-01-17 03:00 local time, which is just before the maximum 32-bit 
signed time_t value.  Previously, that transition was just after the 32-bit 

This change exposed a curious bug in the interaction between zic and localtime.c 
on platforms with signed 32-bit time_t.  For the benefit of older systems, zic 
outputs explicit transitions for years in the range 1900 through 2037, relying 
on the POSIX-TZ-style string (for Pacific/Fiji, "FJT-12FJST,M11.1.0,M1.3.0/3") 
at the end of the tzfile for transitions outside that range.  Although newer 
versions of localtime.c should use the POSIX-TZ-style string for time stamps in 
the year 2038, if time_t is a signed 32-bit integer these localtime.c versions 
mishandle Fijian time stamps from 2038-01-16 14:00:00 through 2038-01-19 
03:14:07 UTC.  This bug is fixed by the attached patch.

I discovered this bug during release testing, and will squeeze the attached 
patch into the next tz release which I plan to make shortly.  It strikes me, 
though, that perhaps zic should be changed too, to better accommodate buggy 
localtime implementations.  If the zic cutoff for old-style transitions is at 
2038-01-19 03:14:08 UTC, rather than at 2038-01-01, buggy localtime code should 
do the right thing.  I don't want to install such a change into zic without some 
thought and/or discussion and/or testing, though; it can wait until a later release.
-------------- next part --------------
From 6d00980f1ff2ac665f3de027c79faacf4f26d1b0 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Thu, 1 Oct 2015 01:43:59 -0700
Subject: [PATCH] Fix signed 32-bit time_t localtime bug on Fiji

* localtime.c (tzloadbody): When on a host with signed 32-bit
time_t, don't skip reading the 64-bit tables, as skipping caused
the code to mishandle Pacific/Fiji in the year 2038.  With the
recent changes for Fiji, there is a transition from +1300 to +1200
at 2018-01-17 03:00 local time.  Since this transition is after
2037, zic omits the explicit transition, which must be calculated
from the POSIX-TZ-style string "FJT-12FJST,M11.1.0,M1.3.0/3" at
the end of the file.  If the code skips reading the 64-bit tables,
it doesn't get to the proper position to see the POSIX-TZ-style
 localtime.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/localtime.c b/localtime.c
index be10749..e3bc763 100644
--- a/localtime.c
+++ b/localtime.c
@@ -556,11 +556,6 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
 		nread -= p - up->buf;
 		memmove(up->buf, p, nread);
-		/*
-		** If this is a signed narrow time_t system, we're done.
-		*/
-		if (TYPE_SIGNED(time_t) && stored >= (int) sizeof(time_t))
-			break;
 	if (doextend && nread > 2 &&
 		up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&

More information about the tz mailing list