(vc-dir-deduce-fileset): Make the error message more explicit.
[bpt/emacs.git] / src / mktime.c
index de5b47d..20c1092 100644 (file)
@@ -1,4 +1,6 @@
-/* Copyright (C) 1993, 94, 95, 96, 97, 98 Free Software Foundation, Inc.
+/* Convert a `struct tm' to a time_t value.
+   Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2002, 2003,
+                 2004, 2005, 2006, 2007  Free Software Foundation, Inc.
    Contributed by Paul Eggert (eggert@twinsun.com).
 
    NOTE: The canonical source of this file is maintained with the GNU C Library.
@@ -16,7 +18,7 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
    USA.  */
 
 /* Define this to have a standalone program to test this implementation of
 # include <config.h>
 #endif
 
-/* Some systems need this in order to declare localtime_r properly.  */
-#ifndef _REENTRANT
-# define _REENTRANT 1
-#endif
-
 #ifdef _LIBC
 # define HAVE_LIMITS_H 1
-# define HAVE_LOCALTIME_R 1
 # define STDC_HEADERS 1
 #endif
 
@@ -62,7 +58,7 @@
 #endif /* DEBUG */
 
 #ifndef __P
-# if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
+# if defined __GNUC__ || (defined __STDC__ && __STDC__)
 #  define __P(args) args
 # else
 #  define __P(args) ()
@@ -114,24 +110,15 @@ const unsigned short int __mon_yday[2][13] =
     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
   };
 
-static struct tm *ranged_convert __P ((struct tm *(*) __P ((const time_t *,
-                                                           struct tm *)),
-                                      time_t *, struct tm *));
-static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *));
-time_t __mktime_internal __P ((struct tm *,
-                              struct tm *(*) (const time_t *, struct tm *),
-                              time_t *));
-
 
 #ifdef _LIBC
-# define localtime_r __localtime_r
+# define my_mktime_localtime_r __localtime_r
 #else
-# if ! HAVE_LOCALTIME_R && ! defined localtime_r
-/* Approximate localtime_r as best we can in its absence.  */
-#  define localtime_r my_mktime_localtime_r
-static struct tm *localtime_r __P ((const time_t *, struct tm *));
+/* If we're a mktime substitute in a GNU program, then prefer
+   localtime to localtime_r, since many localtime_r implementations
+   are buggy.  */
 static struct tm *
-localtime_r (t, tp)
+my_mktime_localtime_r (t, tp)
      const time_t *t;
      struct tm *tp;
 {
@@ -141,7 +128,6 @@ localtime_r (t, tp)
   *tp = *l;
   return tp;
 }
-# endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
 #endif /* ! _LIBC */
 
 
@@ -181,30 +167,16 @@ ydhms_tm_diff (year, yday, hour, min, sec, tp)
     }
 }
 
-
-static time_t localtime_offset;
-
-/* Convert *TP to a time_t value.  */
-time_t
-mktime (tp)
-     struct tm *tp;
-{
-#ifdef _LIBC
-  /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
-     time zone names contained in the external variable `tzname' shall
-     be set as if the tzset() function had been called.  */
-  __tzset ();
-#endif
-
-  return __mktime_internal (tp, localtime_r, &localtime_offset);
-}
-
 /* Use CONVERT to convert *T to a broken down time in *TP.
    If *T is out of range for conversion, adjust it so that
    it is the nearest in-range value and then convert that.  */
 static struct tm *
 ranged_convert (convert, t, tp)
-     struct tm *(*convert) __P ((const time_t *, struct tm *));
+#ifdef PROTOTYPES
+     struct tm *(*convert) (const time_t *, struct tm *);
+#else
+     struct tm *(*convert)();
+#endif
      time_t *t;
      struct tm *tp;
 {
@@ -255,17 +227,21 @@ ranged_convert (convert, t, tp)
 time_t
 __mktime_internal (tp, convert, offset)
      struct tm *tp;
-     struct tm *(*convert) __P ((const time_t *, struct tm *));
+#ifdef PROTOTYPES
+     struct tm *(*convert) (const time_t *, struct tm *);
+#else
+     struct tm *(*convert)();
+#endif
      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
      occur if TP is localtime's returned value and CONVERT is localtime.  */
@@ -277,6 +253,9 @@ __mktime_internal (tp, convert, offset)
   int year_requested = tp->tm_year;
   int isdst = tp->tm_isdst;
 
+  /* 1 if the previous probe was DST.  */
+  int dst2;
+
   /* Ensure that mon is in range, and set year accordingly.  */
   int mon_remainder = mon % 12;
   int negative_mon_remainder = mon_remainder < 0;
@@ -295,6 +274,13 @@ __mktime_internal (tp, convert, offset)
              + mday - 1);
 
   int sec_requested = sec;
+
+  /* Only years after 1970 are defined.
+     If year is 69, it might still be representable due to
+     timezone differences.  */
+  if (year < 69)
+    return -1;
+
 #if LEAP_SECONDS_POSSIBLE
   /* Handle out-of-range seconds specially,
      since ydhms_tm_diff assumes every minute has 60 seconds.  */
@@ -311,36 +297,56 @@ __mktime_internal (tp, convert, offset)
   tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
   t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);
 
-  for (t = t0 + *offset;
+  for (t = t1 = t2 = t0 + *offset, dst2 = 0;
        (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, dst2 = tm.tm_isdst != 0)
+    if (t == t1 && t != t2
+       && (tm.tm_isdst < 0
+           || (isdst < 0
+               ? dst2 <= (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.  (If no tm_isdst
+        was requested and only one of the two values has a nonzero
+        tm_isdst, prefer that 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 && isdst != tm.tm_isdst && 0 <= isdst && 0 <= tm.tm_isdst)
     {
-      int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
-      if (dst_diff)
+      /* tm.tm_isdst has the wrong value.  Look for a neighboring
+        time with the right value, and use its UTC offset.
+        Heuristic: probe the previous three calendar quarters (approximately),
+        looking for the desired isdst.  This isn't perfect,
+        but it's good enough in practice.  */
+      int quarter = 7889238; /* seconds per average 1/4 Gregorian year */
+      int i;
+
+      /* If we're too close to the time_t limit, look in future quarters.  */
+      if (t < TIME_T_MIN + 3 * quarter)
+       quarter = -quarter;
+
+      for (i = 1; i <= 3; i++)
        {
-         /* Move two hours in the direction indicated by the disagreement,
-            probe some more, and switch to a new time if found.
-            The largest known fallback due to daylight savings is two hours:
-            once, in Newfoundland, 1988-10-30 02:00 -> 00:00.  */
-         time_t ot = t - 2 * 60 * 60 * dst_diff;
-         while (--remaining_probes != 0)
+         time_t ot = t - i * quarter;
+         struct tm otm;
+         ranged_convert (convert, &ot, &otm);
+         if (otm.tm_isdst == isdst)
            {
-             struct tm otm;
-             if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
-                                        ranged_convert (convert, &ot, &otm))))
-               {
-                 t = ot;
-                 tm = otm;
-                 break;
-               }
-             if ((ot += dt) == t)
-               break;  /* Avoid a redundant probe.  */
+             /* We found the desired tm_isdst.
+                Extrapolate back to the desired time.  */
+             t = ot + ydhms_tm_diff (year, yday, hour, min, sec, &otm);
+             ranged_convert (convert, &t, &tm);
+             break;
            }
        }
     }
@@ -382,10 +388,36 @@ __mktime_internal (tp, convert, offset)
        return -1;
     }
 
+  if (year == 69)
+    {
+      /* If year was 69, need to check whether the time was representable
+        or not.  */
+      if (t < 0 || t > 2 * 24 * 60 * 60)
+       return -1;
+    }
+
   *tp = tm;
   return t;
 }
 
+
+static time_t localtime_offset;
+
+/* Convert *TP to a time_t value.  */
+time_t
+mktime (tp)
+     struct tm *tp;
+{
+#ifdef _LIBC
+  /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
+     time zone names contained in the external variable `tzname' shall
+     be set as if the tzset() function had been called.  */
+  __tzset ();
+#endif
+
+  return __mktime_internal (tp, my_mktime_localtime_r, &localtime_offset);
+}
+
 #ifdef weak_alias
 weak_alias (mktime, timelocal)
 #endif
@@ -530,6 +562,9 @@ main (argc, argv)
 \f
 /*
 Local Variables:
-compile-command: "gcc -DDEBUG -D__EXTENSIONS__ -DHAVE_LIMITS_H -DHAVE_LOCALTIME_R -DSTDC_HEADERS -Wall -W -O -g mktime.c -o mktime"
+compile-command: "gcc -DDEBUG -DHAVE_LIMITS_H -DSTDC_HEADERS -Wall -W -O -g mktime.c -o mktime"
 End:
 */
+
+/* arch-tag: 9456752f-7ddd-47cb-8286-fa807b1355ae
+   (do not change this comment) */