]> git.ipfire.org Git - thirdparty/glibc.git/blame - time/mktime.c
Update.
[thirdparty/glibc.git] / time / mktime.c
CommitLineData
1dfee75f 1/* Copyright (C) 1993, 94, 95, 96, 97, 98 Free Software Foundation, Inc.
5290baf0 2 This file is part of the GNU C Library.
80fd7387 3 Contributed by Paul Eggert (eggert@twinsun.com).
28f540f4 4
5290baf0
UD
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.
28f540f4 9
5290baf0
UD
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.
28f540f4 14
5290baf0
UD
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. */
28f540f4
RM
19
20/* Define this to have a standalone program to test this implementation of
21 mktime. */
80fd7387 22/* #define DEBUG 1 */
28f540f4
RM
23
24#ifdef HAVE_CONFIG_H
9c2322bc 25# include <config.h>
28f540f4
RM
26#endif
27
1dfee75f 28/* Some systems need this in order to declare localtime_r properly. */
f43ce637
UD
29#ifndef _REENTRANT
30# define _REENTRANT 1
31#endif
32
c0e45674
UD
33#ifdef _LIBC
34# define HAVE_LIMITS_H 1
35# define HAVE_LOCALTIME_R 1
36# define STDC_HEADERS 1
37#endif
38
80fd7387
RM
39/* Assume that leap seconds are possible, unless told otherwise.
40 If the host has a `zic' command with a `-L leapsecondfilename' option,
41 then it supports leap seconds; otherwise it probably doesn't. */
42#ifndef LEAP_SECONDS_POSSIBLE
9c2322bc 43# define LEAP_SECONDS_POSSIBLE 1
80fd7387
RM
44#endif
45
28f540f4
RM
46#include <sys/types.h> /* Some systems define `time_t' here. */
47#include <time.h>
48
c0e45674 49#if HAVE_LIMITS_H
9c2322bc 50# include <limits.h>
80fd7387 51#endif
28f540f4 52
80fd7387 53#if DEBUG
9c2322bc
UD
54# include <stdio.h>
55# if STDC_HEADERS
56# include <stdlib.h>
57# endif
80fd7387 58/* Make it work even if the system's libc has its own mktime routine. */
9c2322bc 59# define mktime my_mktime
80fd7387 60#endif /* DEBUG */
28f540f4
RM
61
62#ifndef __P
9c2322bc
UD
63# if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
64# define __P(args) args
65# else
66# define __P(args) ()
67# endif /* GCC. */
28f540f4
RM
68#endif /* Not __P. */
69
80fd7387 70#ifndef CHAR_BIT
9c2322bc 71# define CHAR_BIT 8
28f540f4
RM
72#endif
73
1dfee75f
UD
74/* The extra casts work around common compiler bugs. */
75#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
76/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
77 It is necessary at least when t == time_t. */
78#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
79 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
36fafd9c 80#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
1dfee75f 81
80fd7387 82#ifndef INT_MIN
1dfee75f 83# define INT_MIN TYPE_MINIMUM (int)
28f540f4 84#endif
80fd7387 85#ifndef INT_MAX
1dfee75f 86# define INT_MAX TYPE_MAXIMUM (int)
28f540f4 87#endif
28f540f4 88
80fd7387 89#ifndef TIME_T_MIN
1dfee75f 90# define TIME_T_MIN TYPE_MINIMUM (time_t)
28f540f4 91#endif
80fd7387 92#ifndef TIME_T_MAX
1dfee75f 93# define TIME_T_MAX TYPE_MAXIMUM (time_t)
28f540f4 94#endif
28f540f4 95
80fd7387
RM
96#define TM_YEAR_BASE 1900
97#define EPOCH_YEAR 1970
28f540f4 98
80fd7387
RM
99#ifndef __isleap
100/* Nonzero if YEAR is a leap year (every 4 years,
101 except every 100th isn't, and every 400th is). */
9c2322bc 102# define __isleap(year) \
80fd7387 103 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
28f540f4 104#endif
28f540f4 105
80fd7387
RM
106/* How many days come before each month (0-12). */
107const unsigned short int __mon_yday[2][13] =
108 {
109 /* Normal years. */
110 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
111 /* Leap years. */
112 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
113 };
28f540f4 114
fe0ec73e
UD
115static struct tm *ranged_convert __P ((struct tm *(*) __P ((const time_t *,
116 struct tm *)),
117 time_t *, struct tm *));
80fd7387
RM
118static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *));
119time_t __mktime_internal __P ((struct tm *,
120 struct tm *(*) (const time_t *, struct tm *),
121 time_t *));
28f540f4 122
28f540f4 123
c2216480 124#ifdef _LIBC
9c2322bc 125# define localtime_r __localtime_r
c2216480 126#else
9c2322bc 127# if ! HAVE_LOCALTIME_R && ! defined localtime_r
c2216480 128/* Approximate localtime_r as best we can in its absence. */
9c2322bc 129# define localtime_r my_mktime_localtime_r
80fd7387 130static struct tm *localtime_r __P ((const time_t *, struct tm *));
c2216480
RM
131static struct tm *
132localtime_r (t, tp)
133 const time_t *t;
134 struct tm *tp;
80fd7387 135{
c2216480
RM
136 struct tm *l = localtime (t);
137 if (! l)
3f33a4ce 138 return 0;
c2216480
RM
139 *tp = *l;
140 return tp;
141}
9c2322bc 142# endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
c0e45674 143#endif /* ! _LIBC */
c2216480 144
80fd7387
RM
145
146/* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
147 measured in seconds, ignoring leap seconds.
148 YEAR uses the same numbering as TM->tm_year.
149 All values are in range, except possibly YEAR.
fe0ec73e 150 If TP is null, return a nonzero value.
80fd7387
RM
151 If overflow occurs, yield the low order bits of the correct answer. */
152static time_t
153ydhms_tm_diff (year, yday, hour, min, sec, tp)
154 int year, yday, hour, min, sec;
155 const struct tm *tp;
156{
fe0ec73e
UD
157 if (!tp)
158 return 1;
159 else
160 {
161 /* Compute intervening leap days correctly even if year is negative.
162 Take care to avoid int overflow. time_t overflow is OK, since
163 only the low order bits of the correct time_t answer are needed.
164 Don't convert to time_t until after all divisions are done, since
165 time_t might be unsigned. */
166 int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
167 int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
168 int a100 = a4 / 25 - (a4 % 25 < 0);
169 int b100 = b4 / 25 - (b4 % 25 < 0);
170 int a400 = a100 >> 2;
171 int b400 = b100 >> 2;
172 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
173 time_t years = year - (time_t) tp->tm_year;
174 time_t days = (365 * years + intervening_leap_days
175 + (yday - tp->tm_yday));
176 return (60 * (60 * (24 * days + (hour - tp->tm_hour))
177 + (min - tp->tm_min))
178 + (sec - tp->tm_sec));
179 }
80fd7387
RM
180}
181
182
be10a868
RM
183static time_t localtime_offset;
184
80fd7387 185/* Convert *TP to a time_t value. */
28f540f4 186time_t
80fd7387
RM
187mktime (tp)
188 struct tm *tp;
28f540f4 189{
5290baf0 190#ifdef _LIBC
9d187dd4
UD
191 /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
192 time zone names contained in the external variable `tzname' shall
193 be set as if the tzset() function had been called. */
194 __tzset ();
5290baf0
UD
195#endif
196
80fd7387 197 return __mktime_internal (tp, localtime_r, &localtime_offset);
28f540f4
RM
198}
199
fe0ec73e
UD
200/* Use CONVERT to convert *T to a broken down time in *TP.
201 If *T is out of range for conversion, adjust it so that
202 it is the nearest in-range value and then convert that. */
203static struct tm *
204ranged_convert (convert, t, tp)
205 struct tm *(*convert) __P ((const time_t *, struct tm *));
206 time_t *t;
207 struct tm *tp;
208{
209 struct tm *r;
210
211 if (! (r = (*convert) (t, tp)) && *t)
212 {
213 time_t bad = *t;
214 time_t ok = 0;
215 struct tm tm;
216
217 /* BAD is a known unconvertible time_t, and OK is a known good one.
218 Use binary search to narrow the range between BAD and OK until
219 they differ by 1. */
220 while (bad != ok + (bad < 0 ? -1 : 1))
221 {
222 time_t mid = *t = (bad < 0
223 ? bad + ((ok - bad) >> 1)
224 : ok + ((bad - ok) >> 1));
225 if ((r = (*convert) (t, tp)))
226 {
227 tm = *r;
228 ok = mid;
229 }
230 else
231 bad = mid;
232 }
233
234 if (!r && ok)
235 {
236 /* The last conversion attempt failed;
237 revert to the most recent successful attempt. */
238 *t = ok;
239 *tp = tm;
240 r = tp;
241 }
242 }
243
244 return r;
245}
246
247
80fd7387
RM
248/* Convert *TP to a time_t value, inverting
249 the monotonic and mostly-unit-linear conversion function CONVERT.
250 Use *OFFSET to keep track of a guess at the offset of the result,
251 compared to what the result would be for UTC without leap seconds.
252 If *OFFSET's guess is correct, only one CONVERT call is needed. */
253time_t
254__mktime_internal (tp, convert, offset)
255 struct tm *tp;
256 struct tm *(*convert) __P ((const time_t *, struct tm *));
257 time_t *offset;
28f540f4 258{
80fd7387
RM
259 time_t t, dt, t0;
260 struct tm tm;
261
262 /* The maximum number of probes (calls to CONVERT) should be enough
263 to handle any combinations of time zone rule changes, solar time,
cc3fa755 264 and leap seconds. POSIX.1 prohibits leap seconds, but some hosts
80fd7387
RM
265 have them anyway. */
266 int remaining_probes = 4;
267
268 /* Time requested. Copy it in case CONVERT modifies *TP; this can
269 occur if TP is localtime's returned value and CONVERT is localtime. */
270 int sec = tp->tm_sec;
271 int min = tp->tm_min;
272 int hour = tp->tm_hour;
273 int mday = tp->tm_mday;
274 int mon = tp->tm_mon;
275 int year_requested = tp->tm_year;
276 int isdst = tp->tm_isdst;
277
278 /* Ensure that mon is in range, and set year accordingly. */
279 int mon_remainder = mon % 12;
280 int negative_mon_remainder = mon_remainder < 0;
281 int mon_years = mon / 12 - negative_mon_remainder;
282 int year = year_requested + mon_years;
283
284 /* The other values need not be in range:
285 the remaining code handles minor overflows correctly,
286 assuming int and time_t arithmetic wraps around.
287 Major overflows are caught at the end. */
288
289 /* Calculate day of year from year, month, and day of month.
290 The result need not be in range. */
291 int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
292 [mon_remainder + 12 * negative_mon_remainder])
293 + mday - 1);
294
9a0a462c 295 int sec_requested = sec;
80fd7387
RM
296#if LEAP_SECONDS_POSSIBLE
297 /* Handle out-of-range seconds specially,
298 since ydhms_tm_diff assumes every minute has 60 seconds. */
80fd7387
RM
299 if (sec < 0)
300 sec = 0;
301 if (59 < sec)
302 sec = 59;
303#endif
304
305 /* Invert CONVERT by probing. First assume the same offset as last time.
306 Then repeatedly use the error to improve the guess. */
307
308 tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
309 tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
310 t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);
28f540f4 311
80fd7387 312 for (t = t0 + *offset;
fe0ec73e
UD
313 (dt = ydhms_tm_diff (year, yday, hour, min, sec,
314 ranged_convert (convert, &t, &tm)));
80fd7387
RM
315 t += dt)
316 if (--remaining_probes == 0)
317 return -1;
318
319 /* Check whether tm.tm_isdst has the requested value, if any. */
320 if (0 <= isdst && 0 <= tm.tm_isdst)
321 {
322 int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
323 if (dst_diff)
28f540f4 324 {
80fd7387
RM
325 /* Move two hours in the direction indicated by the disagreement,
326 probe some more, and switch to a new time if found.
327 The largest known fallback due to daylight savings is two hours:
328 once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */
329 time_t ot = t - 2 * 60 * 60 * dst_diff;
330 while (--remaining_probes != 0)
331 {
332 struct tm otm;
333 if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
fe0ec73e 334 ranged_convert (convert, &ot, &otm))))
80fd7387
RM
335 {
336 t = ot;
337 tm = otm;
338 break;
339 }
340 if ((ot += dt) == t)
341 break; /* Avoid a redundant probe. */
342 }
28f540f4 343 }
80fd7387 344 }
28f540f4 345
80fd7387
RM
346 *offset = t - t0;
347
348#if LEAP_SECONDS_POSSIBLE
349 if (sec_requested != tm.tm_sec)
350 {
351 /* Adjust time to reflect the tm_sec requested, not the normalized value.
352 Also, repair any damage from a false match due to a leap second. */
353 t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
fe0ec73e
UD
354 if (! (*convert) (&t, &tm))
355 return -1;
28f540f4 356 }
80fd7387
RM
357#endif
358
359 if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
28f540f4 360 {
80fd7387
RM
361 /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
362 so check for major overflows. A gross check suffices,
363 since if t has overflowed, it is off by a multiple of
364 TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of
365 the difference that is bounded by a small value. */
366
367 double dyear = (double) year_requested + mon_years - tm.tm_year;
368 double dday = 366 * dyear + mday;
369 double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
370
1dfee75f
UD
371 /* On Irix4.0.5 cc, dividing TIME_T_MIN by 3 does not produce
372 correct results, ie., it erroneously gives a positive value
373 of 715827882. Setting a variable first then doing math on it
374 seems to work. (ghazi@caip.rutgers.edu) */
375
376 const time_t time_t_max = TIME_T_MAX;
377 const time_t time_t_min = TIME_T_MIN;
378
379 if (time_t_max / 3 - time_t_min / 3 < (dsec < 0 ? - dsec : dsec))
80fd7387 380 return -1;
28f540f4 381 }
80fd7387
RM
382
383 *tp = tm;
384 return t;
385}
386
387#ifdef weak_alias
388weak_alias (mktime, timelocal)
28f540f4 389#endif
80fd7387
RM
390\f
391#if DEBUG
28f540f4 392
80fd7387
RM
393static int
394not_equal_tm (a, b)
395 struct tm *a;
396 struct tm *b;
397{
398 return ((a->tm_sec ^ b->tm_sec)
399 | (a->tm_min ^ b->tm_min)
400 | (a->tm_hour ^ b->tm_hour)
401 | (a->tm_mday ^ b->tm_mday)
402 | (a->tm_mon ^ b->tm_mon)
403 | (a->tm_year ^ b->tm_year)
404 | (a->tm_mday ^ b->tm_mday)
405 | (a->tm_yday ^ b->tm_yday)
406 | (a->tm_isdst ^ b->tm_isdst));
407}
28f540f4 408
80fd7387
RM
409static void
410print_tm (tp)
411 struct tm *tp;
412{
fe0ec73e
UD
413 if (tp)
414 printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
415 tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
416 tp->tm_hour, tp->tm_min, tp->tm_sec,
417 tp->tm_yday, tp->tm_wday, tp->tm_isdst);
418 else
419 printf ("0");
80fd7387 420}
28f540f4 421
80fd7387 422static int
fe0ec73e 423check_result (tk, tmk, tl, lt)
80fd7387
RM
424 time_t tk;
425 struct tm tmk;
426 time_t tl;
fe0ec73e 427 struct tm *lt;
80fd7387 428{
fe0ec73e 429 if (tk != tl || !lt || not_equal_tm (&tmk, lt))
80fd7387
RM
430 {
431 printf ("mktime (");
432 print_tm (&tmk);
433 printf (")\nyields (");
fe0ec73e 434 print_tm (lt);
80fd7387
RM
435 printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
436 return 1;
437 }
438
439 return 0;
440}
28f540f4 441
80fd7387
RM
442int
443main (argc, argv)
444 int argc;
445 char **argv;
446{
447 int status = 0;
448 struct tm tm, tmk, tml;
fe0ec73e 449 struct tm *lt;
80fd7387
RM
450 time_t tk, tl;
451 char trailer;
452
453 if ((argc == 3 || argc == 4)
454 && (sscanf (argv[1], "%d-%d-%d%c",
455 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
456 == 3)
457 && (sscanf (argv[2], "%d:%d:%d%c",
458 &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
459 == 3))
460 {
461 tm.tm_year -= TM_YEAR_BASE;
462 tm.tm_mon--;
463 tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
464 tmk = tm;
465 tl = mktime (&tmk);
fe0ec73e
UD
466 lt = localtime (&tl);
467 if (lt)
468 {
469 tml = *lt;
470 lt = &tml;
471 }
80fd7387
RM
472 printf ("mktime returns %ld == ", (long) tl);
473 print_tm (&tmk);
474 printf ("\n");
fe0ec73e 475 status = check_result (tl, tmk, tl, lt);
80fd7387
RM
476 }
477 else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
478 {
479 time_t from = atol (argv[1]);
480 time_t by = atol (argv[2]);
481 time_t to = atol (argv[3]);
28f540f4 482
80fd7387
RM
483 if (argc == 4)
484 for (tl = from; tl <= to; tl += by)
485 {
fe0ec73e
UD
486 lt = localtime (&tl);
487 if (lt)
488 {
489 tmk = tml = *lt;
490 tk = mktime (&tmk);
491 status |= check_result (tk, tmk, tl, tml);
492 }
493 else
494 {
495 printf ("localtime (%ld) yields 0\n", (long) tl);
496 status = 1;
497 }
80fd7387
RM
498 }
499 else
500 for (tl = from; tl <= to; tl += by)
501 {
502 /* Null benchmark. */
fe0ec73e
UD
503 lt = localtime (&tl);
504 if (lt)
505 {
506 tmk = tml = *lt;
507 tk = tl;
508 status |= check_result (tk, tmk, tl, tml);
509 }
510 else
511 {
512 printf ("localtime (%ld) yields 0\n", (long) tl);
513 status = 1;
514 }
80fd7387
RM
515 }
516 }
517 else
518 printf ("Usage:\
519\t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
520\t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
521\t%s FROM BY TO - # Do not test those values (for benchmark).\n",
522 argv[0], argv[0], argv[0]);
523
524 return status;
28f540f4 525}
28f540f4 526
80fd7387 527#endif /* DEBUG */
28f540f4
RM
528\f
529/*
530Local Variables:
fe0ec73e 531compile-command: "gcc -DDEBUG -D__EXTENSIONS__ -DHAVE_LIMITS_H -DHAVE_LOCALTIME_R -DSTDC_HEADERS -Wall -W -O -g mktime.c -o mktime"
28f540f4
RM
532End:
533*/