Formerly ../time/sys/time.h.~16~
[kopensolaris-gnu/glibc.git] / time / mktime.c
index 2afb2f0..07fe494 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1993 Free Software Foundation, Inc.
+/* Copyright (C) 1993, 1994 Free Software Foundation, Inc.
    Contributed by Noel Cragg (noel@cs.oberlin.edu).
 
 This file is part of the GNU C Library.
@@ -18,6 +18,10 @@ License along with the GNU C Library; see the file COPYING.LIB.  If
 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
 Cambridge, MA 02139, USA.  */
 
+/* Define this to have a standalone program to test this implementation of
+   mktime.  */
+/* #define DEBUG */
+
 #ifdef HAVE_CONFIG_H
 #if defined (CONFIG_BROKETS)
 /* We use <config.h> instead of "config.h" so that a compilation
@@ -40,6 +44,13 @@ Cambridge, MA 02139, USA.  */
   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
 #endif
 
+#ifndef __P
+#if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
+#define __P(args) args
+#else
+#define __P(args) ()
+#endif  /* GCC.  */
+#endif  /* Not __P.  */
 
 /* How many days are in each month.  */
 const unsigned short int __mon_lengths[2][12] =
@@ -51,6 +62,10 @@ const unsigned short int __mon_lengths[2][12] =
   };
 
 
+static int times_through_search; /* This library routine should never
+                                   hang -- make sure we always return
+                                   when we're searching for a value */
+
 /* After testing this, the maximum number of iterations that I had on
    any number that I tried was 3!  Not bad.
 
@@ -61,9 +76,12 @@ const unsigned short int __mon_lengths[2][12] =
 
 #ifdef DEBUG
 
+#include <stdio.h>
+#include <ctype.h>
+
 int debugging_enabled = 0;
 
-/* Print the values in a `struct tm'.  */
+/* Print the values in a `struct tm'. */
 static void
 printtm (it)
      struct tm *it;
@@ -82,6 +100,7 @@ printtm (it)
 }
 #endif
 
+
 static time_t
 dist_tm (t1, t2)
      struct tm *t1;
@@ -93,32 +112,44 @@ dist_tm (t1, t2)
 
   v1 = v2 = 0;
 
-#define doit(x, secs)                                                        \
-  v1 += t1->x * secs;                                                        \
-  v2 += t2->x * secs;                                                        \
-  if (!diff_flag)                                                            \
-    {                                                                        \
-      if (t1->x < t2->x)                                                     \
-       diff_flag = -1;                                                       \
-      else if (t1->x > t2->x)                                                \
-       diff_flag = 1;                                                        \
+#define doit(x, secs)                                                         \
+  v1 += t1->x * secs;                                                         \
+  v2 += t2->x * secs;                                                         \
+  if (!diff_flag)                                                             \
+    {                                                                         \
+      if (t1->x < t2->x)                                                      \
+       diff_flag = -1;                                                       \
+      else if (t1->x > t2->x)                                                 \
+       diff_flag = 1;                                                        \
     }
   
-  doit (tm_year, 31536000);    /* Okay, not all years have 365 days.  */
-  doit (tm_mon, 2592000);      /* Okay, not all months have 30 days.  */
+  doit (tm_year, 31536000);    /* Okay, not all years have 365 days. */
+  doit (tm_mon, 2592000);      /* Okay, not all months have 30 days. */
   doit (tm_mday, 86400);
   doit (tm_hour, 3600);
   doit (tm_min, 60);
   doit (tm_sec, 1);
   
 #undef doit
+  
+  /* We should also make sure that the sign of DISTANCE is correct -- if
+     DIFF_FLAG is positive, the distance should be positive and vice versa. */
+  
+  distance = (v1 > v2) ? (v1 - v2) : (v2 - v1);
+  if (diff_flag < 0)
+    distance = -distance;
 
-  distance = v1 - v2;
+  if (times_through_search > 20) /* Arbitrary # of calls, but makes sure we
+                                   never hang if there's a problem with
+                                   this algorithm.  */
+    {
+      distance = diff_flag;
+    }
 
   /* We need this DIFF_FLAG business because it is forseeable that the
      distance may be zero when, in actuality, the two structures are
-     different.  This is usually the case when the dates are 366 days
-     apart and one of the years is a leap year.  */
+     different.  This is usually the case when the dates are 366 days apart
+     and one of the years is a leap year.  */
 
   if (distance == 0 && diff_flag)
     distance = 86400 * diff_flag;
@@ -127,41 +158,46 @@ dist_tm (t1, t2)
 }
       
 
-/* Modified binary search -- make intelligent guesses as to where the time
-   might lie along the timeline, assuming that our target time lies a
-   linear distance (w/o considering time jumps of a particular region).
+/* Modified b-search -- make intelligent guesses as to where the time might
+   lie along the timeline, assuming that our target time lies a linear
+   distance (w/o considering time jumps of a particular region).
 
    Assume that time does not fluctuate at all along the timeline -- e.g.,
    assume that a day will always take 86400 seconds, etc. -- and come up
    with a hypothetical value for the time_t representation of the struct tm
-   TARGET, in relation to the guess variable -- it should be pretty close!  */
+   TARGET, in relation to the guess variable -- it should be pretty close! */
 
 static time_t
-search (target)
+search (target, producer)
      struct tm *target;
+     struct tm *(*producer) __P ((const time_t *));
 {
   struct tm *guess_tm;
   time_t guess = 0;
   time_t distance = 0;
 
+  times_through_search = 0;
+
   do
     {
       guess += distance;
 
-      guess_tm = localtime (&guess);
+      times_through_search++;     
+      
+      guess_tm = (*producer) (&guess);
       
 #ifdef DEBUG
       if (debugging_enabled)
        {
-         printf ("guess %d == ", guess);
+         printf ("guess %d == ", (int) guess);
          printtm (guess_tm);
          puts ("");
        }
 #endif
       
-      /* Are we on the money?  */
+      /* Are we on the money? */
       distance = dist_tm (target, guess_tm);
-
+      
     } while (distance != 0);
 
   return guess;
@@ -171,14 +207,14 @@ search (target)
    be passing their `struct tm *' right from localtime, let's make a copy
    for ourselves and run the search on the copy.
 
-   Also, we have to normalize the timeptr because it's possible to call mktime
-   with values that are out of range for a specific item (like 30th Feb).  */
-
+   Also, we have to normalize *TIMEPTR because it's possible to call mktime
+   with values that are out of range for a specific item (like Feb 30th). */
 time_t
-mktime (timeptr)
+_mktime_internal (timeptr, producer)
      struct tm *timeptr;
+     struct tm *(*producer) __P ((const time_t *));
 {
-  struct tm private_mktime_struct_tm; /* Yes, users can get a ptr to this.  */
+  struct tm private_mktime_struct_tm; /* Yes, users can get a ptr to this. */
   struct tm *me;
   time_t result;
 
@@ -202,16 +238,16 @@ mktime (timeptr)
   normalize (tm_min, 0, 59, tm_hour);
   normalize (tm_hour, 0, 23, tm_mday);
   
-  /* Do the month first, so day range can be found.  */
+  /* Do the month first, so day range can be found. */
   normalize (tm_mon, 0, 11, tm_year);
   normalize (tm_mday, 1,
             __mon_lengths[__isleap (me->tm_year)][me->tm_mon],
             tm_mon);
 
-  /* Do the month again, because the day may have pushed it out of range.  */
+  /* Do the month again, because the day may have pushed it out of range. */
   normalize (tm_mon, 0, 11, tm_year);
 
-  /* Do day again, because month may have changed the range.  */
+  /* Do the day again, because the month may have changed the range. */
   normalize (tm_mday, 1,
             __mon_lengths[__isleap (me->tm_year)][me->tm_mon],
             tm_mon);
@@ -225,12 +261,19 @@ mktime (timeptr)
     }
 #endif
 
-  result = search (me);
+  result = search (me, producer);
 
   *timeptr = *me;
 
   return result;
 }
+
+time_t
+mktime (timeptr)
+     struct tm *timeptr;
+{
+  return _mktime_internal (timeptr, localtime);
+}
 \f
 #ifdef DEBUG
 void
@@ -252,7 +295,7 @@ main (argc, argv)
        {
          struct tm *tm = localtime (&q);
          if ((q % 10000) == 0) { printf ("%ld\n", q); fflush (stdout); }
-         if (q != my_mktime (tm))
+         if (q != mktime (tm))
            { printf ("failed for %ld\n", q); fflush (stdout); }
        }
       
@@ -267,18 +310,18 @@ main (argc, argv)
       exit (0);
     }
   
-  debugging_enabled = 1;       /* we want to see the info */
+  debugging_enabled = 1;       /* We want to see the info */
 
   ++argv;
   time = atoi (*argv);
   
-  printf ("Time: %d %s\n", time, ctime (&time));
+  printf ("Time: %d %s\n", time, ctime ((time_t *) &time));
 
-  tmptr = localtime (&time);
+  tmptr = localtime ((time_t *) &time);
   printf ("localtime returns: ");
   printtm (tmptr);
   printf ("\n");
-  printf ("mktime: %d\n\n", mktime (tmptr));
+  printf ("mktime: %d\n\n", (int) mktime (tmptr));
 
   tmptr->tm_sec -= 20;
   tmptr->tm_min -= 20;
@@ -286,8 +329,8 @@ main (argc, argv)
   tmptr->tm_mday -= 20;
   tmptr->tm_mon -= 20;
   tmptr->tm_year -= 20;
-  tmptr->tm_gmtoff -= 20000;   /* this has no effect! */
-  tmptr->tm_zone = NULL;       /* nor does this! */
+  tmptr->tm_gmtoff -= 20000;   /* This has no effect! */
+  tmptr->tm_zone = NULL;       /* Nor does this! */
   tmptr->tm_isdst = -1;
 
   printf ("changed ranges: ");
@@ -295,6 +338,6 @@ main (argc, argv)
   printf ("\n\n");
 
   result_time = mktime (tmptr);
-  printf ("\n  mine: %d\n", result_time);
+  printf ("\nmktime: %d\n", result_time);
 }
 #endif /* DEBUG */