[tz] [PROPOSED PATCH] Fix localtime bug with Anchorage after 2037

Paul Eggert eggert at cs.ucla.edu
Fri Aug 28 08:19:31 UTC 2015


* NEWS: Document this.
* localtime.c (tzloadbody): Handle America/Anchorage after 2037,
even though the sum of the abbreviations' sizes (42) plus the sum
of the extended abbreviations' sizes (10) exceeds TZ_MAX_CHARS (50).
Do this by reusing existing abbreviations.  Thanks to Bradley
White for reporting the bug.  Perhaps we should also consider
increasing TZ_MAX_CHARS from its currently-low value.
---
 NEWS        |  8 +++++++
 localtime.c | 71 +++++++++++++++++++++++++++++++++++++++----------------------
 2 files changed, 54 insertions(+), 25 deletions(-)

diff --git a/NEWS b/NEWS
index b477250..ffe3fb2 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,13 @@
 News for the tz database
 
+Unreleased, experimental changes
+
+  Changes affecting code
+
+    localtime no longer mishandles America/Anchorage after 2037.
+    (Thanks to Bradley White for reporting the bug.)
+
+
 Release 2015f - 2015-08-10 18:06:56 -0700
 
   Changes affecting future time stamps
diff --git a/localtime.c b/localtime.c
index ded8f7b..ac34e5d 100644
--- a/localtime.c
+++ b/localtime.c
@@ -569,31 +569,52 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
 
 			up->buf[nread - 1] = '\0';
 			if (tzparse(&up->buf[1], ts, false)
-			    && ts->typecnt == 2
-			    && sp->charcnt + ts->charcnt <= TZ_MAX_CHARS) {
-					for (i = 0; i < 2; ++i)
-						ts->ttis[i].tt_abbrind +=
-							sp->charcnt;
-					for (i = 0; i < ts->charcnt; ++i)
-						sp->chars[sp->charcnt++] =
-							ts->chars[i];
-					i = 0;
-					while (i < ts->timecnt &&
-						ts->ats[i] <=
-						sp->ats[sp->timecnt - 1])
-							++i;
-					while (i < ts->timecnt &&
-					    sp->timecnt < TZ_MAX_TIMES) {
-						sp->ats[sp->timecnt] =
-							ts->ats[i];
-						sp->types[sp->timecnt] =
-							sp->typecnt +
-							ts->types[i];
-						++sp->timecnt;
-						++i;
-					}
-					sp->ttis[sp->typecnt++] = ts->ttis[0];
-					sp->ttis[sp->typecnt++] = ts->ttis[1];
+			    && ts->typecnt == 2) {
+
+			  /* 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 example.  */
+			  int gotabbr = 0;
+			  int charcnt = sp->charcnt;
+			  for (i = 0; i < 2; i++) {
+			    char *tsabbr = ts->chars + ts->ttis[i].tt_abbrind;
+			    int j;
+			    for (j = 0; j < charcnt; j++)
+			      if (strcmp(sp->chars + j, tsabbr) == 0) {
+				ts->ttis[i].tt_abbrind = j;
+				gotabbr++;
+				break;
+			      }
+			    if (! (j < charcnt)) {
+			      int tsabbrlen = strlen(tsabbr);
+			      if (j + tsabbrlen < TZ_MAX_CHARS) {
+				strcpy(sp->chars + j, tsabbr);
+				charcnt = j + tsabbrlen + 1;
+				ts->ttis[i].tt_abbrind = j;
+				gotabbr++;
+			      }
+			    }
+			  }
+			  if (gotabbr == 2) {
+			    sp->charcnt = charcnt;
+			    for (i = 0; i < ts->timecnt; i++)
+			      if (sp->ats[sp->timecnt - 1] < ts->ats[i])
+				break;
+			    while (i < ts->timecnt
+				   && sp->timecnt < TZ_MAX_TIMES) {
+			      sp->ats[sp->timecnt] = ts->ats[i];
+			      sp->types[sp->timecnt] = (sp->typecnt
+							+ ts->types[i]);
+			      sp->timecnt++;
+			      i++;
+			    }
+			    sp->ttis[sp->typecnt++] = ts->ttis[0];
+			    sp->ttis[sp->typecnt++] = ts->ttis[1];
+			  }
 			}
 	}
 	if (sp->timecnt > 1) {
-- 
2.1.4



More information about the tz mailing list