1 /* Copyright (C) 1993, 94, 95, 96, 97, 98 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
28 /* Some systems need this in order to declare localtime_r properly. */
34 # define HAVE_LIMITS_H 1
35 # define HAVE_LOCALTIME_R 1
36 # define STDC_HEADERS 1
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
43 # define LEAP_SECONDS_POSSIBLE 1
46 #include <sys/types.h> /* Some systems define `time_t' here. */
58 /* Make it work even if the system's libc has its own mktime routine. */
59 # define mktime my_mktime
63 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
64 # define __P(args) args
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))
80 #define TYPE_MAXIMUM(t) (~ (t) 0 - TYPE_MINIMUM (t))
83 # define INT_MIN TYPE_MINIMUM (int)
86 # define INT_MAX TYPE_MAXIMUM (int)
90 # define TIME_T_MIN TYPE_MINIMUM (time_t)
93 # define TIME_T_MAX TYPE_MAXIMUM (time_t)
96 #define TM_YEAR_BASE 1900
97 #define EPOCH_YEAR 1970
100 /* Nonzero if YEAR is a leap year (every 4 years,
101 except every 100th isn't, and every 400th is). */
102 # define __isleap(year) \
103 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
106 /* How many days come before each month (0-12). */
107 const unsigned short int __mon_yday
[2][13] =
110 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
112 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
115 static struct tm
*ranged_convert
__P ((struct tm
*(*) __P ((const time_t *,
117 time_t *, struct tm
*));
118 static time_t ydhms_tm_diff
__P ((int, int, int, int, int, const struct tm
*));
119 time_t __mktime_internal
__P ((struct tm
*,
120 struct tm
*(*) (const time_t *, struct tm
*),
125 # define localtime_r __localtime_r
127 # if ! HAVE_LOCALTIME_R && ! defined localtime_r
128 /* Approximate localtime_r as best we can in its absence. */
129 # define localtime_r my_mktime_localtime_r
130 static struct tm
*localtime_r
__P ((const time_t *, struct tm
*));
136 struct tm
*l
= localtime (t
);
142 # endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
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.
150 If TP is null, return a nonzero value.
151 If overflow occurs, yield the low order bits of the correct answer. */
153 ydhms_tm_diff (year
, yday
, hour
, min
, sec
, tp
)
154 int year
, yday
, hour
, min
, sec
;
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
));
183 static time_t localtime_offset
;
185 /* Convert *TP to a time_t value. */
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. */
197 return __mktime_internal (tp
, localtime_r
, &localtime_offset
);
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. */
204 ranged_convert (convert
, t
, tp
)
205 struct tm
*(*convert
) __P ((const time_t *, struct tm
*));
211 if (! (r
= (*convert
) (t
, tp
)) && *t
)
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
220 while (bad
!= ok
+ (bad
< 0 ? -1 : 1))
222 time_t mid
= *t
= (bad
< 0
223 ? bad
+ ((ok
- bad
) >> 1)
224 : ok
+ ((bad
- ok
) >> 1));
225 if ((r
= (*convert
) (t
, tp
)))
236 /* The last conversion attempt failed;
237 revert to the most recent successful attempt. */
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. */
254 __mktime_internal (tp
, convert
, offset
)
256 struct tm
*(*convert
) __P ((const time_t *, struct tm
*));
262 /* The maximum number of probes (calls to CONVERT) should be enough
263 to handle any combinations of time zone rule changes, solar time,
264 and leap seconds. POSIX.1 prohibits leap seconds, but some hosts
266 int remaining_probes
= 4;
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
;
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
;
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. */
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
])
295 int sec_requested
= sec
;
296 #if LEAP_SECONDS_POSSIBLE
297 /* Handle out-of-range seconds specially,
298 since ydhms_tm_diff assumes every minute has 60 seconds. */
305 /* Invert CONVERT by probing. First assume the same offset as last time.
306 Then repeatedly use the error to improve the guess. */
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
);
312 for (t
= t0
+ *offset
;
313 (dt
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
,
314 ranged_convert (convert
, &t
, &tm
)));
316 if (--remaining_probes
== 0)
319 /* Check whether tm.tm_isdst has the requested value, if any. */
320 if (0 <= isdst
&& 0 <= tm
.tm_isdst
)
322 int dst_diff
= (isdst
!= 0) - (tm
.tm_isdst
!= 0);
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)
333 if (! (dt
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
,
334 ranged_convert (convert
, &ot
, &otm
))))
341 break; /* Avoid a redundant probe. */
348 #if LEAP_SECONDS_POSSIBLE
349 if (sec_requested
!= tm
.tm_sec
)
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);
354 if (! (*convert
) (&t
, &tm
))
359 if (TIME_T_MAX
/ INT_MAX
/ 366 / 24 / 60 / 60 < 3)
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. */
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
;
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) */
376 const time_t time_t_max
= TIME_T_MAX
;
377 const time_t time_t_min
= TIME_T_MIN
;
379 if (time_t_max
/ 3 - time_t_min
/ 3 < (dsec
< 0 ? - dsec
: dsec
))
388 weak_alias (mktime
, timelocal
)
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
));
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
);
423 check_result (tk
, tmk
, tl
, lt
)
429 if (tk
!= tl
|| !lt
|| not_equal_tm (&tmk
, lt
))
433 printf (")\nyields (");
435 printf (") == %ld, should be %ld\n", (long) tl
, (long) tk
);
448 struct tm tm
, tmk
, tml
;
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
)
457 && (sscanf (argv
[2], "%d:%d:%d%c",
458 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
, &trailer
)
461 tm
.tm_year
-= TM_YEAR_BASE
;
463 tm
.tm_isdst
= argc
== 3 ? -1 : atoi (argv
[3]);
466 lt
= localtime (&tl
);
472 printf ("mktime returns %ld == ", (long) tl
);
475 status
= check_result (tl
, tmk
, tl
, lt
);
477 else if (argc
== 4 || (argc
== 5 && strcmp (argv
[4], "-") == 0))
479 time_t from
= atol (argv
[1]);
480 time_t by
= atol (argv
[2]);
481 time_t to
= atol (argv
[3]);
484 for (tl
= from
; tl
<= to
; tl
+= by
)
486 lt
= localtime (&tl
);
491 status
|= check_result (tk
, tmk
, tl
, tml
);
495 printf ("localtime (%ld) yields 0\n", (long) tl
);
500 for (tl
= from
; tl
<= to
; tl
+= by
)
502 /* Null benchmark. */
503 lt
= localtime (&tl
);
508 status
|= check_result (tk
, tmk
, tl
, tml
);
512 printf ("localtime (%ld) yields 0\n", (long) tl
);
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]);
531 compile-command: "gcc -DDEBUG -D__EXTENSIONS__ -DHAVE_LIMITS_H -DHAVE_LOCALTIME_R -DSTDC_HEADERS -Wall -W -O -g mktime.c -o mktime"