Additional files to distribute.
[glibc/history.git] / time / mktime.c
blob501288896793625bd7c0bf86c55b31f6d7f86360
1 /* Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Paul Eggert (eggert@twinsun.com).
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
20 /* Define this to have a standalone program to test this implementation of
21 mktime. */
22 /* #define DEBUG 1 */
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
28 #ifdef _LIBC
29 # define HAVE_LIMITS_H 1
30 # define HAVE_LOCALTIME_R 1
31 # define STDC_HEADERS 1
32 #endif
34 /* Assume that leap seconds are possible, unless told otherwise.
35 If the host has a `zic' command with a `-L leapsecondfilename' option,
36 then it supports leap seconds; otherwise it probably doesn't. */
37 #ifndef LEAP_SECONDS_POSSIBLE
38 # define LEAP_SECONDS_POSSIBLE 1
39 #endif
41 #include <sys/types.h> /* Some systems define `time_t' here. */
42 #include <time.h>
44 #if HAVE_LIMITS_H
45 # include <limits.h>
46 #endif
48 #if DEBUG
49 # include <stdio.h>
50 # if STDC_HEADERS
51 # include <stdlib.h>
52 # endif
53 /* Make it work even if the system's libc has its own mktime routine. */
54 # define mktime my_mktime
55 #endif /* DEBUG */
57 #ifndef __P
58 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
59 # define __P(args) args
60 # else
61 # define __P(args) ()
62 # endif /* GCC. */
63 #endif /* Not __P. */
65 #ifndef CHAR_BIT
66 # define CHAR_BIT 8
67 #endif
69 #ifndef INT_MIN
70 # define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
71 #endif
72 #ifndef INT_MAX
73 # define INT_MAX (~0 - INT_MIN)
74 #endif
76 #ifndef TIME_T_MIN
77 /* The outer cast to time_t works around a bug in Cray C 5.0.3.0. */
78 # define TIME_T_MIN ((time_t) \
79 (0 < (time_t) -1 ? (time_t) 0 \
80 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1)))
81 #endif
82 #ifndef TIME_T_MAX
83 # define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
84 #endif
86 #define TM_YEAR_BASE 1900
87 #define EPOCH_YEAR 1970
89 #ifndef __isleap
90 /* Nonzero if YEAR is a leap year (every 4 years,
91 except every 100th isn't, and every 400th is). */
92 # define __isleap(year) \
93 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
94 #endif
96 /* How many days come before each month (0-12). */
97 const unsigned short int __mon_yday[2][13] =
99 /* Normal years. */
100 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
101 /* Leap years. */
102 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
105 static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *));
106 time_t __mktime_internal __P ((struct tm *,
107 struct tm *(*) (const time_t *, struct tm *),
108 time_t *));
111 #ifdef _LIBC
112 # define localtime_r __localtime_r
113 #else
114 # if ! HAVE_LOCALTIME_R && ! defined localtime_r
115 /* Approximate localtime_r as best we can in its absence. */
116 # define localtime_r my_mktime_localtime_r
117 static struct tm *localtime_r __P ((const time_t *, struct tm *));
118 static struct tm *
119 localtime_r (t, tp)
120 const time_t *t;
121 struct tm *tp;
123 struct tm *l = localtime (t);
124 if (! l)
125 return 0;
126 *tp = *l;
127 return tp;
129 # endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
130 #endif /* ! _LIBC */
133 /* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
134 measured in seconds, ignoring leap seconds.
135 YEAR uses the same numbering as TM->tm_year.
136 All values are in range, except possibly YEAR.
137 If overflow occurs, yield the low order bits of the correct answer. */
138 static time_t
139 ydhms_tm_diff (year, yday, hour, min, sec, tp)
140 int year, yday, hour, min, sec;
141 const struct tm *tp;
143 /* Compute intervening leap days correctly even if year is negative.
144 Take care to avoid int overflow. time_t overflow is OK, since
145 only the low order bits of the correct time_t answer are needed.
146 Don't convert to time_t until after all divisions are done, since
147 time_t might be unsigned. */
148 int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
149 int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
150 int a100 = a4 / 25 - (a4 % 25 < 0);
151 int b100 = b4 / 25 - (b4 % 25 < 0);
152 int a400 = a100 >> 2;
153 int b400 = b100 >> 2;
154 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
155 time_t years = year - (time_t) tp->tm_year;
156 time_t days = (365 * years + intervening_leap_days
157 + (yday - tp->tm_yday));
158 return (60 * (60 * (24 * days + (hour - tp->tm_hour))
159 + (min - tp->tm_min))
160 + (sec - tp->tm_sec));
164 static time_t localtime_offset;
166 /* Convert *TP to a time_t value. */
167 time_t
168 mktime (tp)
169 struct tm *tp;
171 #ifdef _LIBC
172 /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
173 time zone names contained in the external variable `tzname' shall
174 be set as if the tzset() function had been called. */
175 __tzset ();
176 #endif
178 return __mktime_internal (tp, localtime_r, &localtime_offset);
181 /* Convert *TP to a time_t value, inverting
182 the monotonic and mostly-unit-linear conversion function CONVERT.
183 Use *OFFSET to keep track of a guess at the offset of the result,
184 compared to what the result would be for UTC without leap seconds.
185 If *OFFSET's guess is correct, only one CONVERT call is needed. */
186 time_t
187 __mktime_internal (tp, convert, offset)
188 struct tm *tp;
189 struct tm *(*convert) __P ((const time_t *, struct tm *));
190 time_t *offset;
192 time_t t, dt, t0;
193 struct tm tm;
195 /* The maximum number of probes (calls to CONVERT) should be enough
196 to handle any combinations of time zone rule changes, solar time,
197 and leap seconds. Posix.1 prohibits leap seconds, but some hosts
198 have them anyway. */
199 int remaining_probes = 4;
201 /* Time requested. Copy it in case CONVERT modifies *TP; this can
202 occur if TP is localtime's returned value and CONVERT is localtime. */
203 int sec = tp->tm_sec;
204 int min = tp->tm_min;
205 int hour = tp->tm_hour;
206 int mday = tp->tm_mday;
207 int mon = tp->tm_mon;
208 int year_requested = tp->tm_year;
209 int isdst = tp->tm_isdst;
211 /* Ensure that mon is in range, and set year accordingly. */
212 int mon_remainder = mon % 12;
213 int negative_mon_remainder = mon_remainder < 0;
214 int mon_years = mon / 12 - negative_mon_remainder;
215 int year = year_requested + mon_years;
217 /* The other values need not be in range:
218 the remaining code handles minor overflows correctly,
219 assuming int and time_t arithmetic wraps around.
220 Major overflows are caught at the end. */
222 /* Calculate day of year from year, month, and day of month.
223 The result need not be in range. */
224 int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
225 [mon_remainder + 12 * negative_mon_remainder])
226 + mday - 1);
228 #if LEAP_SECONDS_POSSIBLE
229 /* Handle out-of-range seconds specially,
230 since ydhms_tm_diff assumes every minute has 60 seconds. */
231 int sec_requested = sec;
232 if (sec < 0)
233 sec = 0;
234 if (59 < sec)
235 sec = 59;
236 #endif
238 /* Invert CONVERT by probing. First assume the same offset as last time.
239 Then repeatedly use the error to improve the guess. */
241 tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
242 tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
243 t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);
245 for (t = t0 + *offset;
246 (dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm)));
247 t += dt)
248 if (--remaining_probes == 0)
249 return -1;
251 /* Check whether tm.tm_isdst has the requested value, if any. */
252 if (0 <= isdst && 0 <= tm.tm_isdst)
254 int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
255 if (dst_diff)
257 /* Move two hours in the direction indicated by the disagreement,
258 probe some more, and switch to a new time if found.
259 The largest known fallback due to daylight savings is two hours:
260 once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */
261 time_t ot = t - 2 * 60 * 60 * dst_diff;
262 while (--remaining_probes != 0)
264 struct tm otm;
265 if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
266 (*convert) (&ot, &otm))))
268 t = ot;
269 tm = otm;
270 break;
272 if ((ot += dt) == t)
273 break; /* Avoid a redundant probe. */
278 *offset = t - t0;
280 #if LEAP_SECONDS_POSSIBLE
281 if (sec_requested != tm.tm_sec)
283 /* Adjust time to reflect the tm_sec requested, not the normalized value.
284 Also, repair any damage from a false match due to a leap second. */
285 t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
286 (*convert) (&t, &tm);
288 #endif
290 if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
292 /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
293 so check for major overflows. A gross check suffices,
294 since if t has overflowed, it is off by a multiple of
295 TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of
296 the difference that is bounded by a small value. */
298 double dyear = (double) year_requested + mon_years - tm.tm_year;
299 double dday = 366 * dyear + mday;
300 double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
302 if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec))
303 return -1;
306 *tp = tm;
307 return t;
310 #ifdef weak_alias
311 weak_alias (mktime, timelocal)
312 #endif
314 #if DEBUG
316 static int
317 not_equal_tm (a, b)
318 struct tm *a;
319 struct tm *b;
321 return ((a->tm_sec ^ b->tm_sec)
322 | (a->tm_min ^ b->tm_min)
323 | (a->tm_hour ^ b->tm_hour)
324 | (a->tm_mday ^ b->tm_mday)
325 | (a->tm_mon ^ b->tm_mon)
326 | (a->tm_year ^ b->tm_year)
327 | (a->tm_mday ^ b->tm_mday)
328 | (a->tm_yday ^ b->tm_yday)
329 | (a->tm_isdst ^ b->tm_isdst));
332 static void
333 print_tm (tp)
334 struct tm *tp;
336 printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
337 tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
338 tp->tm_hour, tp->tm_min, tp->tm_sec,
339 tp->tm_yday, tp->tm_wday, tp->tm_isdst);
342 static int
343 check_result (tk, tmk, tl, tml)
344 time_t tk;
345 struct tm tmk;
346 time_t tl;
347 struct tm tml;
349 if (tk != tl || not_equal_tm (&tmk, &tml))
351 printf ("mktime (");
352 print_tm (&tmk);
353 printf (")\nyields (");
354 print_tm (&tml);
355 printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
356 return 1;
359 return 0;
363 main (argc, argv)
364 int argc;
365 char **argv;
367 int status = 0;
368 struct tm tm, tmk, tml;
369 time_t tk, tl;
370 char trailer;
372 if ((argc == 3 || argc == 4)
373 && (sscanf (argv[1], "%d-%d-%d%c",
374 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
375 == 3)
376 && (sscanf (argv[2], "%d:%d:%d%c",
377 &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
378 == 3))
380 tm.tm_year -= TM_YEAR_BASE;
381 tm.tm_mon--;
382 tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
383 tmk = tm;
384 tl = mktime (&tmk);
385 tml = *localtime (&tl);
386 printf ("mktime returns %ld == ", (long) tl);
387 print_tm (&tmk);
388 printf ("\n");
389 status = check_result (tl, tmk, tl, tml);
391 else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
393 time_t from = atol (argv[1]);
394 time_t by = atol (argv[2]);
395 time_t to = atol (argv[3]);
397 if (argc == 4)
398 for (tl = from; tl <= to; tl += by)
400 tml = *localtime (&tl);
401 tmk = tml;
402 tk = mktime (&tmk);
403 status |= check_result (tk, tmk, tl, tml);
405 else
406 for (tl = from; tl <= to; tl += by)
408 /* Null benchmark. */
409 tml = *localtime (&tl);
410 tmk = tml;
411 tk = tl;
412 status |= check_result (tk, tmk, tl, tml);
415 else
416 printf ("Usage:\
417 \t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
418 \t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
419 \t%s FROM BY TO - # Do not test those values (for benchmark).\n",
420 argv[0], argv[0], argv[0]);
422 return status;
425 #endif /* DEBUG */
428 Local Variables:
429 compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"
430 End: