This is the mail archive of the libc-hacker@sourceware.cygnus.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: BUG in mktime (PR libc/783)


   From: Ulrich Drepper <drepper@cygnus.com>
   Date: 05 Oct 1998 18:44:26 -0700

   The problem is that mktime returns on other systems (e.g, Solaris)
   for the illegal broken down date Apr 5 2:00 a non-error
   value.... With glibc it results in [an error].  I think the glibc
   interpretation is at least equally well.

True, but in practice the glibc mktime interpretation makes date
arithmetic a bit painful.  (I can say this, since I wrote mktime.  :-)

It turns out that glibc mktime is the only mktime I know of that
rejects times in a spring-forward gap.  Other mktimes (Solaris, Olson,
etc.) assume that if you request such a time in within a N-second
spring-forward gap, you get the time T that is N seconds away from the
requested time, such that T's tm_isdst is not equal to the requested
tm_isdst.  This is typically more convenient in practice.

Here's a patch to GNU mktime that causes it to behave like other
mktimes in this situation.  With this patch, you shouldn't need
to alter GNU `date' to fix this particular example.

1998-10-08  Paul Eggert  <eggert@twinsun.com>

	* mktime.c (__mktime_internal): When the requested time falls
	in a spring-forward gap of size DT, return a time that is DT
	away from the requested time, preferring a time whose tm_isdst
	differs from the requested value.  Bump the max number of
	probes from 4 to 6 to account for the extra probes needed to
	discover a spring-forward gap in the worst case.

--- mktime.c	1998/09/08 04:59:12	20.3.0.1
+++ mktime.c	1998/10/08 07:19:29	20.3.0.2
@@ -259,12 +259,12 @@
      time_t *offset;
 {
-  time_t t, dt, t0;
+  time_t t, dt, t0, t1, t2;
   struct tm tm;
 
   /* The maximum number of probes (calls to CONVERT) should be enough
      to handle any combinations of time zone rule changes, solar time,
-     and leap seconds.  POSIX.1 prohibits leap seconds, but some hosts
-     have them anyway.  */
-  int remaining_probes = 4;
+     leap seconds, and oscillations around a spring-forward gap.
+     POSIX.1 prohibits leap seconds, but some hosts have them anyway.  */
+  int remaining_probes = 6;
 
   /* Time requested.  Copy it in case CONVERT modifies *TP; this can
@@ -312,13 +312,25 @@
   t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);
 
-  for (t = t0 + *offset;
+  for (t = t1 = t2 = t0 + *offset;
        (dt = ydhms_tm_diff (year, yday, hour, min, sec,
 			    ranged_convert (convert, &t, &tm)));
-       t += dt)
-    if (--remaining_probes == 0)
+       t1 = t2, t2 = t, t += dt)
+    if (t == t1 && t != t2
+	&& (isdst < 0 || tm.tm_isdst < 0
+	    || (isdst != 0) != (tm.tm_isdst != 0)))
+      /* We can't possibly find a match, as we are oscillating
+	 between two values.  The requested time probably falls
+	 within a spring-forward gap of size DT.  Follow the common
+	 practice in this case, which is to return a time that is DT
+	 away from the requested time, preferring a time whose
+	 tm_isdst differs from the requested value.  In practice,
+	 this is more useful than returning -1.  */
+      break;
+    else if (--remaining_probes == 0)
       return -1;
 
-  /* Check whether tm.tm_isdst has the requested value, if any.  */
-  if (0 <= isdst && 0 <= tm.tm_isdst)
+  /* If we have a match, check whether tm.tm_isdst has the requested
+     value, if any.  */
+  if (dt == 0 && 0 <= isdst && 0 <= tm.tm_isdst)
     {
       int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]