about summary refs log tree commit diff
path: root/time
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2023-01-05 18:21:25 +0100
committerFlorian Weimer <fweimer@redhat.com>2023-01-05 18:22:55 +0100
commit35141f304e319109c322f797ae71c0b9420ccb05 (patch)
treed0b3dc256825087141dd07824a98a224a58ef420 /time
parent8f27dc1af5238adbc14000f073178ee4a2c2b3cf (diff)
downloadglibc-35141f304e319109c322f797ae71c0b9420ccb05.tar.gz
glibc-35141f304e319109c322f797ae71c0b9420ccb05.tar.xz
glibc-35141f304e319109c322f797ae71c0b9420ccb05.zip
time: Set daylight to 1 for matching DST/offset change (bug 29951)
The daylight variable is supposed to be set to 1 if DST is ever in
use for the current time zone.  But __tzfile_read used to do this:

  __daylight = rule_stdoff != rule_dstoff;

This check can fail to set __daylight to 1 if the DST and non-DST
offsets happen to be the same.
Diffstat (limited to 'time')
-rw-r--r--time/tzfile.c41
1 files changed, 22 insertions, 19 deletions
diff --git a/time/tzfile.c b/time/tzfile.c
index 394b098856..8bba4e5b8d 100644
--- a/time/tzfile.c
+++ b/time/tzfile.c
@@ -61,6 +61,10 @@ static size_t num_leaps;
 static struct leap *leaps;
 static char *tzspec;
 
+/* Used to restore the daylight variable during time conversion, as if
+   tzset had been called.  */
+static int daylight_saved;
+
 #include <endian.h>
 #include <byteswap.h>
 
@@ -438,36 +442,35 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
   if (__tzname[1] == NULL)
     __tzname[1] = __tzname[0];
 
+  daylight_saved = 0;
   if (num_transitions == 0)
     /* Use the first rule (which should also be the only one).  */
     rule_stdoff = rule_dstoff = types[0].offset;
   else
     {
-      int stdoff_set = 0, dstoff_set = 0;
-      rule_stdoff = rule_dstoff = 0;
+      rule_stdoff = 0;
+
+      /* Search for the last rule with a standard time offset.  This
+	 will be used for the global timezone variable.  */
       i = num_transitions - 1;
       do
-	{
-	  if (!stdoff_set && !types[type_idxs[i]].isdst)
-	    {
-	      stdoff_set = 1;
-	      rule_stdoff = types[type_idxs[i]].offset;
-	    }
-	  else if (!dstoff_set && types[type_idxs[i]].isdst)
-	    {
-	      dstoff_set = 1;
-	      rule_dstoff = types[type_idxs[i]].offset;
-	    }
-	  if (stdoff_set && dstoff_set)
+	if (!types[type_idxs[i]].isdst)
+	  {
+	    rule_stdoff = types[type_idxs[i]].offset;
 	    break;
-	}
+	  }
+	else
+	  daylight_saved = 1;
       while (i-- > 0);
 
-      if (!dstoff_set)
-	rule_dstoff = rule_stdoff;
+      /* Keep searching to see if there is a DST rule.  This
+	 information will be used to set the global daylight
+	 variable.  */
+      while (i-- > 0 && !daylight_saved)
+	daylight_saved = types[type_idxs[i]].isdst;
     }
 
-  __daylight = rule_stdoff != rule_dstoff;
+  __daylight = daylight_saved;
   __timezone = -rule_stdoff;
 
  done:
@@ -731,7 +734,7 @@ __tzfile_compute (__time64_t timer, int use_localtime,
 	}
 
       struct ttinfo *info = &types[i];
-      __daylight = rule_stdoff != rule_dstoff;
+      __daylight = daylight_saved;
       __timezone = -rule_stdoff;
 
       if (__tzname[0] == NULL)