]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - time/tzfile.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / time / tzfile.c
index b118e07dc41dae18266b0e0c8360f0c6dad99e73..bcb408fcdba2c4d24e45d9085930c5d9d418ecb4 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991-1993,1995-2001,2003,2004 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2015 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -12,9 +12,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #include <assert.h>
 #include <limits.h>
@@ -25,6 +24,7 @@
 #include <time.h>
 #include <unistd.h>
 #include <sys/stat.h>
+#include <stdint.h>
 
 #define        NOID
 #include <timezone/tzfile.h>
@@ -32,6 +32,7 @@
 int __use_tzfile;
 static dev_t tzfile_dev;
 static ino64_t tzfile_ino;
+static time_t tzfile_mtime;
 
 struct ttinfo
   {
@@ -48,7 +49,6 @@ struct leap
     long int change;           /* Seconds of correction to apply.  */
   };
 
-static struct ttinfo *find_transition (time_t timer) internal_function;
 static void compute_tzname_max (size_t) internal_function;
 
 static size_t num_transitions;
@@ -61,6 +61,7 @@ static long int rule_stdoff;
 static long int rule_dstoff;
 static size_t num_leaps;
 static struct leap *leaps;
+static char *tzspec;
 
 #include <endian.h>
 #include <byteswap.h>
@@ -70,50 +71,62 @@ static inline int
 __attribute ((always_inline))
 decode (const void *ptr)
 {
-  if ((BYTE_ORDER == BIG_ENDIAN) && sizeof (int) == 4)
+  if (BYTE_ORDER == BIG_ENDIAN && sizeof (int) == 4)
     return *(const int *) ptr;
-  else if (BYTE_ORDER == LITTLE_ENDIAN && sizeof (int) == 4)
+  if (sizeof (int) == 4)
     return bswap_32 (*(const int *) ptr);
-  else
-    {
-      const unsigned char *p = ptr;
-      int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0;
 
-      result = (result << 8) | *p++;
-      result = (result << 8) | *p++;
-      result = (result << 8) | *p++;
-      result = (result << 8) | *p++;
+  const unsigned char *p = ptr;
+  int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0;
 
-      return result;
-    }
+  result = (result << 8) | *p++;
+  result = (result << 8) | *p++;
+  result = (result << 8) | *p++;
+  result = (result << 8) | *p++;
+
+  return result;
+}
+
+
+static inline int64_t
+__attribute ((always_inline))
+decode64 (const void *ptr)
+{
+  if ((BYTE_ORDER == BIG_ENDIAN))
+    return *(const int64_t *) ptr;
+
+  return bswap_64 (*(const int64_t *) ptr);
 }
 
+
 void
 __tzfile_read (const char *file, size_t extra, char **extrap)
 {
   static const char default_tzdir[] = TZDIR;
   size_t num_isstd, num_isgmt;
-  register FILE *f;
+  FILE *f;
   struct tzhead tzhead;
   size_t chars;
-  register size_t i;
+  size_t i;
   size_t total_size;
   size_t types_idx;
   size_t leaps_idx;
   int was_using_tzfile = __use_tzfile;
+  int trans_width = 4;
+  size_t tzspec_len;
+  char *new = NULL;
 
-  __use_tzfile = 0;
+  if (sizeof (time_t) != 4 && sizeof (time_t) != 8)
+    abort ();
 
-  if (transitions != NULL)
-    free ((void *) transitions);
-  transitions = NULL;
+  __use_tzfile = 0;
 
   if (file == NULL)
     /* No user specification; use the site-wide default.  */
     file = TZDEFAULT;
   else if (*file == '\0')
     /* User specified the empty string; use UTC with no leap seconds.  */
-    return;
+    goto ret_free_transitions;
   else
     {
       /* We must not allow to read an arbitrary file in a setuid
@@ -127,61 +140,57 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
              || strstr (file, "../") != NULL))
        /* This test is certainly a bit too restrictive but it should
           catch all critical cases.  */
-       return;
+       goto ret_free_transitions;
     }
 
   if (*file != '/')
     {
       const char *tzdir;
-      unsigned int len, tzdir_len;
-      char *new, *tmp;
 
       tzdir = getenv ("TZDIR");
       if (tzdir == NULL || *tzdir == '\0')
-       {
-         tzdir = default_tzdir;
-         tzdir_len = sizeof (default_tzdir) - 1;
-       }
-      else
-       tzdir_len = strlen (tzdir);
-      len = strlen (file) + 1;
-      new = (char *) __alloca (tzdir_len + 1 + len);
-      tmp = __mempcpy (new, tzdir, tzdir_len);
-      *tmp++ = '/';
-      memcpy (tmp, file, len);
+       tzdir = default_tzdir;
+      if (__asprintf (&new, "%s/%s", tzdir, file) == -1)
+       goto ret_free_transitions;
       file = new;
     }
 
+  /* If we were already using tzfile, check whether the file changed.  */
+  struct stat64 st;
+  if (was_using_tzfile
+      && stat64 (file, &st) == 0
+      && tzfile_ino == st.st_ino && tzfile_dev == st.st_dev
+      && tzfile_mtime == st.st_mtime)
+    goto done;  /* Nothing to do.  */
+
   /* Note the file is opened with cancellation in the I/O functions
-     disabled.  */
-  f = fopen (file, "rc");
+     disabled and if available FD_CLOEXEC set.  */
+  f = fopen (file, "rce");
   if (f == NULL)
-    return;
+    goto ret_free_transitions;
 
-  /* Get information about the file.  */
-  struct stat64 st;
-  if (fstat64 (fileno (f), &st) != 0)
+  /* Get information about the file we are actually using.  */
+  if (fstat64 (__fileno (f), &st) != 0)
     {
       fclose (f);
-      return;
-    }
-  if (was_using_tzfile && tzfile_ino == st.st_ino && tzfile_dev == st.st_dev)
-    {
-      /* It's the same file.  No further work needed.  */
-      fclose (f);
-      __use_tzfile = 1;
-      return;
+      goto ret_free_transitions;
     }
 
-  /* Remember the inode and device number.  */
+  free ((void *) transitions);
+  transitions = NULL;
+
+  /* Remember the inode and device number and modification time.  */
   tzfile_dev = st.st_dev;
   tzfile_ino = st.st_ino;
+  tzfile_mtime = st.st_mtime;
 
   /* No threads reading this stream.  */
   __fsetlocking (f, FSETLOCKING_BYCALLER);
 
-  if (__builtin_expect (fread_unlocked ((void *) &tzhead, sizeof (tzhead),
-                                       1, f) != 1, 0))
+ read_again:
+  if (__builtin_expect (__fread_unlocked ((void *) &tzhead, sizeof (tzhead),
+                                         1, f) != 1, 0)
+      || memcmp (tzhead.tzh_magic, TZ_MAGIC, sizeof (tzhead.tzh_magic)) != 0)
     goto lose;
 
   num_transitions = (size_t) decode (tzhead.tzh_timecnt);
@@ -191,19 +200,82 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
   num_isstd = (size_t) decode (tzhead.tzh_ttisstdcnt);
   num_isgmt = (size_t) decode (tzhead.tzh_ttisgmtcnt);
 
+  /* For platforms with 64-bit time_t we use the new format if available.  */
+  if (sizeof (time_t) == 8 && trans_width == 4
+      && tzhead.tzh_version[0] != '\0')
+    {
+      /* We use the 8-byte format.  */
+      trans_width = 8;
+
+      /* Position the stream before the second header.  */
+      size_t to_skip = (num_transitions * (4 + 1)
+                       + num_types * 6
+                       + chars
+                       + num_leaps * 8
+                       + num_isstd
+                       + num_isgmt);
+      if (fseek (f, to_skip, SEEK_CUR) != 0)
+       goto lose;
+
+      goto read_again;
+    }
+
+  if (__builtin_expect (num_transitions
+                       > ((SIZE_MAX - (__alignof__ (struct ttinfo) - 1))
+                          / (sizeof (time_t) + 1)), 0))
+    goto lose;
   total_size = num_transitions * (sizeof (time_t) + 1);
   total_size = ((total_size + __alignof__ (struct ttinfo) - 1)
                & ~(__alignof__ (struct ttinfo) - 1));
   types_idx = total_size;
-  total_size += num_types * sizeof (struct ttinfo) + chars;
+  if (__builtin_expect (num_types
+                       > (SIZE_MAX - total_size) / sizeof (struct ttinfo), 0))
+    goto lose;
+  total_size += num_types * sizeof (struct ttinfo);
+  if (__glibc_unlikely (chars > SIZE_MAX - total_size))
+    goto lose;
+  total_size += chars;
+  if (__builtin_expect (__alignof__ (struct leap) - 1
+                       > SIZE_MAX - total_size, 0))
+    goto lose;
   total_size = ((total_size + __alignof__ (struct leap) - 1)
                & ~(__alignof__ (struct leap) - 1));
   leaps_idx = total_size;
+  if (__builtin_expect (num_leaps
+                       > (SIZE_MAX - total_size) / sizeof (struct leap), 0))
+    goto lose;
   total_size += num_leaps * sizeof (struct leap);
-  /* This is for the extra memory required by the caller.  */
-  total_size += extra;
+  tzspec_len = 0;
+  if (sizeof (time_t) == 8 && trans_width == 8)
+    {
+      off_t rem = st.st_size - __ftello (f);
+      if (__builtin_expect (rem < 0
+                           || (size_t) rem < (num_transitions * (8 + 1)
+                                              + num_types * 6
+                                              + chars), 0))
+       goto lose;
+      tzspec_len = (size_t) rem - (num_transitions * (8 + 1)
+                                  + num_types * 6
+                                  + chars);
+      if (__builtin_expect (num_leaps > SIZE_MAX / 12
+                           || tzspec_len < num_leaps * 12, 0))
+       goto lose;
+      tzspec_len -= num_leaps * 12;
+      if (__glibc_unlikely (tzspec_len < num_isstd))
+       goto lose;
+      tzspec_len -= num_isstd;
+      if (__glibc_unlikely (tzspec_len == 0 || tzspec_len - 1 < num_isgmt))
+       goto lose;
+      tzspec_len -= num_isgmt + 1;
+      if (__glibc_unlikely (SIZE_MAX - total_size < tzspec_len))
+       goto lose;
+    }
+  if (__glibc_unlikely (SIZE_MAX - total_size - tzspec_len < extra))
+    goto lose;
 
-  transitions = (time_t *) malloc (total_size);
+  /* Allocate enough memory including the extra block requested by the
+     caller.  */
+  transitions = (time_t *) malloc (total_size + tzspec_len + extra);
   if (transitions == NULL)
     goto lose;
 
@@ -212,35 +284,39 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
   types = (struct ttinfo *) ((char *) transitions + types_idx);
   zone_names = (char *) types + num_types * sizeof (struct ttinfo);
   leaps = (struct leap *) ((char *) transitions + leaps_idx);
+  if (sizeof (time_t) == 8 && trans_width == 8)
+    tzspec = (char *) leaps + num_leaps * sizeof (struct leap) + extra;
+  else
+    tzspec = NULL;
   if (extra > 0)
     *extrap = (char *) &leaps[num_leaps];
 
-  if (sizeof (time_t) < 4)
-    abort ();
-
-  if (sizeof (time_t) == 4)
+  if (sizeof (time_t) == 4 || __builtin_expect (trans_width == 8, 1))
     {
-      if (__builtin_expect (fread_unlocked (transitions, 1,
-                                           (4 + 1) * num_transitions, f)
-                           != (4 + 1) * num_transitions, 0))
+      if (__builtin_expect (__fread_unlocked (transitions, trans_width + 1,
+                                             num_transitions, f)
+                           != num_transitions, 0))
        goto lose;
     }
   else
     {
-      if (__builtin_expect (fread_unlocked (transitions, 4, num_transitions, f)
+      if (__builtin_expect (__fread_unlocked (transitions, 4,
+                                             num_transitions, f)
                            != num_transitions, 0)
-         || __builtin_expect (fread_unlocked (type_idxs, 1, num_transitions,
-                                              f) != num_transitions, 0))
+         || __builtin_expect (__fread_unlocked (type_idxs, 1, num_transitions,
+                                                f) != num_transitions, 0))
        goto lose;
     }
 
   /* Check for bogus indices in the data file, so we can hereafter
      safely use type_idxs[T] as indices into `types' and never crash.  */
   for (i = 0; i < num_transitions; ++i)
-    if (__builtin_expect (type_idxs[i] >= num_types, 0))
+    if (__glibc_unlikely (type_idxs[i] >= num_types))
       goto lose;
 
-  if (BYTE_ORDER != BIG_ENDIAN || sizeof (time_t) != 4)
+  if ((BYTE_ORDER != BIG_ENDIAN && (sizeof (time_t) == 4 || trans_width == 4))
+      || (BYTE_ORDER == BIG_ENDIAN && sizeof (time_t) == 8
+         && trans_width == 4))
     {
       /* Decode the transition times, stored as 4-byte integers in
         network (big-endian) byte order.  We work from the end of
@@ -250,38 +326,49 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
       while (i-- > 0)
        transitions[i] = decode ((char *) transitions + i * 4);
     }
+  else if (BYTE_ORDER != BIG_ENDIAN && sizeof (time_t) == 8)
+    {
+      /* Decode the transition times, stored as 8-byte integers in
+        network (big-endian) byte order.  */
+      for (i = 0; i < num_transitions; ++i)
+       transitions[i] = decode64 ((char *) transitions + i * 8);
+    }
 
   for (i = 0; i < num_types; ++i)
     {
       unsigned char x[4];
       int c;
-      if (__builtin_expect (fread_unlocked (x, 1, sizeof (x), f) != sizeof (x),
+      if (__builtin_expect (__fread_unlocked (x, 1,
+                                             sizeof (x), f) != sizeof (x),
                            0))
        goto lose;
       c = getc_unlocked (f);
-      if (__builtin_expect ((unsigned int) c > 1u, 0))
+      if (__glibc_unlikely ((unsigned int) c > 1u))
        goto lose;
       types[i].isdst = c;
       c = getc_unlocked (f);
-      if (__builtin_expect ((size_t) c > chars, 0))
+      if (__glibc_unlikely ((size_t) c > chars))
        /* Bogus index in data file.  */
        goto lose;
       types[i].idx = c;
       types[i].offset = (long int) decode (x);
     }
 
-  if (__builtin_expect (fread_unlocked (zone_names, 1, chars, f) != chars, 0))
+  if (__glibc_unlikely (__fread_unlocked (zone_names, 1, chars, f) != chars))
     goto lose;
 
   for (i = 0; i < num_leaps; ++i)
     {
-      unsigned char x[4];
-      if (__builtin_expect (fread_unlocked (x, 1, sizeof (x), f) != sizeof (x),
-                           0))
+      unsigned char x[8];
+      if (__builtin_expect (__fread_unlocked (x, 1, trans_width, f)
+                           != trans_width, 0))
        goto lose;
-      leaps[i].transition = (time_t) decode (x);
-      if (__builtin_expect (fread_unlocked (x, 1, sizeof (x), f) != sizeof (x),
-                           0))
+      if (sizeof (time_t) == 4 || trans_width == 4)
+       leaps[i].transition = (time_t) decode (x);
+      else
+       leaps[i].transition = (time_t) decode64 (x);
+
+      if (__glibc_unlikely (__fread_unlocked (x, 1, 4, f) != 4))
        goto lose;
       leaps[i].change = (long int) decode (x);
     }
@@ -289,7 +376,7 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
   for (i = 0; i < num_isstd; ++i)
     {
       int c = getc_unlocked (f);
-      if (__builtin_expect (c == EOF, 0))
+      if (__glibc_unlikely (c == EOF))
        goto lose;
       types[i].isstd = c != 0;
     }
@@ -299,13 +386,67 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
   for (i = 0; i < num_isgmt; ++i)
     {
       int c = getc_unlocked (f);
-      if (__builtin_expect (c == EOF, 0))
+      if (__glibc_unlikely (c == EOF))
        goto lose;
       types[i].isgmt = c != 0;
     }
   while (i < num_types)
     types[i++].isgmt = 0;
 
+  /* Read the POSIX TZ-style information if possible.  */
+  if (sizeof (time_t) == 8 && tzspec != NULL)
+    {
+      /* Skip over the newline first.  */
+      if (getc_unlocked (f) != '\n'
+         || (__fread_unlocked (tzspec, 1, tzspec_len - 1, f)
+             != tzspec_len - 1))
+       tzspec = NULL;
+      else
+       tzspec[tzspec_len - 1] = '\0';
+    }
+  else if (sizeof (time_t) == 4 && tzhead.tzh_version[0] != '\0')
+    {
+      /* Get the TZ string.  */
+      if (__builtin_expect (__fread_unlocked ((void *) &tzhead,
+                                             sizeof (tzhead), 1, f) != 1, 0)
+         || (memcmp (tzhead.tzh_magic, TZ_MAGIC, sizeof (tzhead.tzh_magic))
+             != 0))
+       goto lose;
+
+      size_t num_transitions2 = (size_t) decode (tzhead.tzh_timecnt);
+      size_t num_types2 = (size_t) decode (tzhead.tzh_typecnt);
+      size_t chars2 = (size_t) decode (tzhead.tzh_charcnt);
+      size_t num_leaps2 = (size_t) decode (tzhead.tzh_leapcnt);
+      size_t num_isstd2 = (size_t) decode (tzhead.tzh_ttisstdcnt);
+      size_t num_isgmt2 = (size_t) decode (tzhead.tzh_ttisgmtcnt);
+
+      /* Position the stream before the second header.  */
+      size_t to_skip = (num_transitions2 * (8 + 1)
+                       + num_types2 * 6
+                       + chars2
+                       + num_leaps2 * 12
+                       + num_isstd2
+                       + num_isgmt2);
+      off_t off;
+      if (fseek (f, to_skip, SEEK_CUR) != 0
+         || (off = __ftello (f)) < 0
+         || st.st_size < off + 2)
+       goto lose;
+
+      tzspec_len = st.st_size - off - 1;
+      char *tzstr = alloca (tzspec_len);
+      if (getc_unlocked (f) != '\n'
+         || (__fread_unlocked (tzstr, 1, tzspec_len - 1, f)
+             != tzspec_len - 1))
+       goto lose;
+      tzstr[tzspec_len - 1] = '\0';
+      tzspec = __tzstring (tzstr);
+    }
+
+  /* Don't use an empty TZ string.  */
+  if (tzspec != NULL && tzspec[0] == '\0')
+    tzspec = NULL;
+
   fclose (f);
 
   /* First "register" all timezone names.  */
@@ -376,11 +517,17 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
   __daylight = rule_stdoff != rule_dstoff;
   __timezone = -rule_stdoff;
 
+ done:
   __use_tzfile = 1;
+  free (new);
   return;
 
  lose:
   fclose (f);
+ ret_free_transitions:
+  free (new);
+  free ((void *) transitions);
+  transitions = NULL;
 }
 \f
 /* The user specified a hand-made timezone, but not its DST rules.
@@ -471,64 +618,163 @@ __tzfile_default (const char *std, const char *dst,
   compute_tzname_max (stdlen + dstlen);
 }
 \f
-static struct ttinfo *
-internal_function
-find_transition (time_t timer)
-{
-  size_t i;
-
-  if (num_transitions == 0 || timer < transitions[0])
-    {
-      /* TIMER is before any transition (or there are no transitions).
-        Choose the first non-DST type
-        (or the first if they're all DST types).  */
-      i = 0;
-      while (i < num_types && types[i].isdst)
-       ++i;
-      if (i == num_types)
-       i = 0;
-    }
-  else
-    {
-      /* Find the first transition after TIMER, and
-        then pick the type of the transition before it.  */
-      for (i = 1; i < num_transitions; ++i)
-       if (timer < transitions[i])
-         break;
-      i = type_idxs[i - 1];
-    }
-
-  return &types[i];
-}
-\f
 void
 __tzfile_compute (time_t timer, int use_localtime,
                  long int *leap_correct, int *leap_hit,
                  struct tm *tp)
 {
-  register size_t i;
+  size_t i;
 
   if (use_localtime)
     {
-      struct ttinfo *info = find_transition (timer);
-      __daylight = rule_stdoff != rule_dstoff;
-      __timezone = -rule_stdoff;
       __tzname[0] = NULL;
       __tzname[1] = NULL;
-      for (i = num_transitions; i > 0; )
+
+      if (__glibc_unlikely (num_transitions == 0 || timer < transitions[0]))
        {
-         int type = type_idxs[--i];
-         int dst = types[type].isdst;
-         int idx = types[type].idx;
+         /* TIMER is before any transition (or there are no transitions).
+            Choose the first non-DST type
+            (or the first if they're all DST types).  */
+         i = 0;
+         while (i < num_types && types[i].isdst)
+           {
+             if (__tzname[1] == NULL)
+               __tzname[1] = __tzstring (&zone_names[types[i].idx]);
+
+             ++i;
+           }
+
+         if (i == num_types)
+           i = 0;
+         __tzname[0] = __tzstring (&zone_names[types[i].idx]);
+         if (__tzname[1] == NULL)
+           {
+             size_t j = i;
+             while (j < num_types)
+               if (types[j].isdst)
+                 {
+                   __tzname[1] = __tzstring (&zone_names[types[j].idx]);
+                   break;
+                 }
+               else
+                 ++j;
+           }
+       }
+      else if (__glibc_unlikely (timer >= transitions[num_transitions - 1]))
+       {
+         if (__glibc_unlikely (tzspec == NULL))
+           {
+           use_last:
+             i = num_transitions;
+             goto found;
+           }
+
+         /* Parse the POSIX TZ-style string.  */
+         __tzset_parse_tz (tzspec);
+
+         /* Convert to broken down structure.  If this fails do not
+            use the string.  */
+         if (__glibc_unlikely (! __offtime (&timer, 0, tp)))
+           goto use_last;
+
+         /* Use the rules from the TZ string to compute the change.  */
+         __tz_compute (timer, tp, 1);
+
+         /* If tzspec comes from posixrules loaded by __tzfile_default,
+            override the STD and DST zone names with the ones user
+            requested in TZ envvar.  */
+         if (__glibc_unlikely (zone_names == (char *) &leaps[num_leaps]))
+           {
+             assert (num_types == 2);
+             __tzname[0] = __tzstring (zone_names);
+             __tzname[1] = __tzstring (&zone_names[strlen (zone_names) + 1]);
+           }
+
+         goto leap;
+       }
+      else
+       {
+         /* Find the first transition after TIMER, and
+            then pick the type of the transition before it.  */
+         size_t lo = 0;
+         size_t hi = num_transitions - 1;
+         /* Assume that DST is changing twice a year and guess initial
+            search spot from it.
+            Half of a gregorian year has on average 365.2425 * 86400 / 2
+            = 15778476 seconds.  */
+         i = (transitions[num_transitions - 1] - timer) / 15778476;
+         if (i < num_transitions)
+           {
+             i = num_transitions - 1 - i;
+             if (timer < transitions[i])
+               {
+                 if (i < 10 || timer >= transitions[i - 10])
+                   {
+                     /* Linear search.  */
+                     while (timer < transitions[i - 1])
+                       --i;
+                     goto found;
+                   }
+                 hi = i - 10;
+               }
+             else
+               {
+                 if (i + 10 >= num_transitions || timer < transitions[i + 10])
+                   {
+                     /* Linear search.  */
+                     while (timer >= transitions[i])
+                       ++i;
+                     goto found;
+                   }
+                 lo = i + 10;
+               }
+           }
 
-         if (__tzname[dst] == NULL)
+         /* Binary search.  */
+         /* assert (timer >= transitions[lo] && timer < transitions[hi]); */
+         while (lo + 1 < hi)
+           {
+             i = (lo + hi) / 2;
+             if (timer < transitions[i])
+               hi = i;
+             else
+               lo = i;
+           }
+         i = hi;
+
+       found:
+         /* assert (timer >= transitions[i - 1]
+            && (i == num_transitions || timer < transitions[i])); */
+         __tzname[types[type_idxs[i - 1]].isdst]
+           = __tzstring (&zone_names[types[type_idxs[i - 1]].idx]);
+         size_t j = i;
+         while (j < num_transitions)
            {
-             __tzname[dst] = __tzstring (&zone_names[idx]);
+             int type = type_idxs[j];
+             int dst = types[type].isdst;
+             int idx = types[type].idx;
+
+             if (__tzname[dst] == NULL)
+               {
+                 __tzname[dst] = __tzstring (&zone_names[idx]);
 
-             if (__tzname[1 - dst] != NULL)
-               break;
+                 if (__tzname[1 - dst] != NULL)
+                   break;
+               }
+
+             ++j;
            }
+
+         if (__glibc_unlikely (__tzname[0] == NULL))
+           __tzname[0] = __tzname[1];
+
+         i = type_idxs[i - 1];
        }
+
+      struct ttinfo *info = &types[i];
+      __daylight = rule_stdoff != rule_dstoff;
+      __timezone = -rule_stdoff;
+
       if (__tzname[0] == NULL)
        {
          /* This should only happen if there are no transition rules.
@@ -540,10 +786,12 @@ __tzfile_compute (time_t timer, int use_localtime,
        /* There is no daylight saving time.  */
        __tzname[1] = __tzname[0];
       tp->tm_isdst = info->isdst;
-      tp->tm_zone = __tzstring (&zone_names[info->idx]);
+      assert (strcmp (&zone_names[info->idx], __tzname[tp->tm_isdst]) == 0);
+      tp->tm_zone = __tzname[tp->tm_isdst];
       tp->tm_gmtoff = info->offset;
     }
 
+ leap:
   *leap_correct = 0L;
   *leap_hit = 0;