1 /* Copyright (C) 1993, 1994 Free Software Foundation, Inc.
2 Contributed by Noel Cragg (noel@cs.oberlin.edu), with fixes
3 by Michael E. Calwas (calwas@ttd.teradyne.com).
5 This file is part of the GNU C Library.
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public
18 License along with the GNU C Library; see the file COPYING.LIB. If
19 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
20 Cambridge, MA 02139, USA. */
22 /* Define this to have a standalone program to test this implementation of
27 #if defined (CONFIG_BROKETS)
28 /* We use <config.h> instead of "config.h" so that a compilation
29 using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
30 (which it would do because it found this file in $srcdir). */
37 #include <sys/types.h> /* Some systems define `time_t' here. */
42 /* Nonzero if YEAR is a leap year (every 4 years,
43 except every 100th isn't, and every 400th is). */
44 #define __isleap(year) \
45 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
49 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
50 #define __P(args) args
56 /* How many days are in each month. */
57 const unsigned short int __mon_lengths[2][12] =
60 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
62 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
66 static int times_through_search; /* This library routine should never
67 hang -- make sure we always return
68 when we're searching for a value */
70 /* After testing this, the maximum number of iterations that I had on
71 any number that I tried was 3! Not bad.
73 mktime converts a `struct tm' (broken-down local time) into a `time_t';
74 it is the opposite of localtime. It is possible to put the following
75 values out of range and have mktime compensate: tm_sec, tm_min, tm_hour,
76 tm_mday, tm_year. The other values in the structure are ignored. */
83 int debugging_enabled = 0;
85 /* Print the values in a `struct tm'. */
90 printf ("%d/%d/%d %d:%d:%d (%s) yday:%d f:%d o:%ld",
111 unsigned long int v1, v2;
116 #define doit(x, secs) \
117 v1 += t1->x * secs; \
118 v2 += t2->x * secs; \
123 else if (t1->x > t2->x) \
127 doit (tm_year, 31536000); /* Okay, not all years have 365 days. */
128 doit (tm_mon, 2592000); /* Okay, not all months have 30 days. */
129 doit (tm_mday, 86400);
130 doit (tm_hour, 3600);
136 /* We should also make sure that the sign of DISTANCE is correct -- if
137 DIFF_FLAG is positive, the distance should be positive and vice versa. */
139 distance = (v1 > v2) ? (v1 - v2) : (v2 - v1);
141 distance = -distance;
143 if (times_through_search > 20) /* Arbitrary # of calls, but makes sure we
144 never hang if there's a problem with
147 distance = diff_flag;
150 /* We need this DIFF_FLAG business because it is forseeable that the
151 distance may be zero when, in actuality, the two structures are
152 different. This is usually the case when the dates are 366 days apart
153 and one of the years is a leap year. */
155 if (distance == 0 && diff_flag)
156 distance = 86400 * diff_flag;
162 /* Modified b-search -- make intelligent guesses as to where the time might
163 lie along the timeline, assuming that our target time lies a linear
164 distance (w/o considering time jumps of a particular region).
166 Assume that time does not fluctuate at all along the timeline -- e.g.,
167 assume that a day will always take 86400 seconds, etc. -- and come up
168 with a hypothetical value for the time_t representation of the struct tm
169 TARGET, in relation to the guess variable -- it should be pretty close! */
172 search (target, producer)
174 struct tm *(*producer) __P ((const time_t *));
180 times_through_search = 0;
186 times_through_search++;
188 guess_tm = (*producer) (&guess);
191 if (debugging_enabled)
193 printf ("guess %d == ", (int) guess);
199 /* Are we on the money? */
200 distance = dist_tm (target, guess_tm);
202 } while (distance != 0);
207 /* Since this function will call localtime many times (and the user might
208 be passing their `struct tm *' right from localtime, let's make a copy
209 for ourselves and run the search on the copy.
211 Also, we have to normalize *TIMEPTR because it's possible to call mktime
212 with values that are out of range for a specific item (like Feb 30th). */
214 _mktime_internal (timeptr, producer)
216 struct tm *(*producer) __P ((const time_t *));
218 struct tm private_mktime_struct_tm; /* Yes, users can get a ptr to this. */
222 me = &private_mktime_struct_tm;
226 #define normalize(foo,x,y,bar); \
227 while (me->foo < x) \
230 me->foo = (y - (x - me->foo) + 1); \
232 while (me->foo > y) \
234 me->foo = (x + (me->foo - y) - 1); \
238 normalize (tm_sec, 0, 59, tm_min);
239 normalize (tm_min, 0, 59, tm_hour);
240 normalize (tm_hour, 0, 23, tm_mday);
242 /* Do the month first, so day range can be found. */
243 normalize (tm_mon, 0, 11, tm_year);
245 /* Since the day range modifies the month, we should be careful how
246 we reference the array of month lengths -- it is possible that
247 the month will go negative, hence the %... */
248 normalize (tm_mday, 1,
249 __mon_lengths[__isleap (me->tm_year)][((me->tm_mon < 0)
250 ? (12 + (me->tm_mon % 12))
251 : (me->tm_mon % 12)) ],
254 /* Do the month again, because the day may have pushed it out of range. */
255 normalize (tm_mon, 0, 11, tm_year);
257 /* Do the day again, because the month may have changed the range. */
258 normalize (tm_mday, 1,
259 __mon_lengths[__isleap (me->tm_year)][((me->tm_mon < 0)
260 ? (12 + (me->tm_mon % 12))
261 : (me->tm_mon % 12)) ],
265 if (debugging_enabled)
267 printf ("After normalizing: ");
273 result = search (me, producer);
284 return _mktime_internal (timeptr, localtime);
301 printf ("starting long test...\n");
303 for (q = 10000000; q < 1000000000; q++)
305 struct tm *tm = localtime (&q);
306 if ((q % 10000) == 0) { printf ("%ld\n", q); fflush (stdout); }
307 if (q != mktime (tm))
308 { printf ("failed for %ld\n", q); fflush (stdout); }
311 printf ("test finished\n");
318 printf ("wrong # of args\n");
322 debugging_enabled = 1; /* We want to see the info */
327 printf ("Time: %d %s\n", time, ctime ((time_t *) &time));
329 tmptr = localtime ((time_t *) &time);
330 printf ("localtime returns: ");
333 printf ("mktime: %d\n\n", (int) mktime (tmptr));
337 tmptr->tm_hour -= 20;
338 tmptr->tm_mday -= 20;
340 tmptr->tm_year -= 20;
341 tmptr->tm_gmtoff -= 20000; /* This has no effect! */
342 tmptr->tm_zone = NULL; /* Nor does this! */
343 tmptr->tm_isdst = -1;
345 printf ("changed ranges: ");
349 result_time = mktime (tmptr);
350 printf ("\nmktime: %d\n", result_time);