1 /* Copyright (C) 1993, 94, 95, 96, 97, 98 Free Software Foundation, Inc.
2 Contributed by Paul Eggert (eggert@twinsun.com).
4 NOTE: The canonical source of this file is maintained with the GNU C Library.
5 Bugs can be reported to bug-glibc@gnu.org.
7 This program is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
12 This program 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
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
22 /* Define this to have a standalone program to test this implementation of
30 /* Some systems need this in order to declare localtime_r properly. */
36 # define HAVE_LIMITS_H 1
37 # define HAVE_LOCALTIME_R 1
38 # define STDC_HEADERS 1
41 /* Assume that leap seconds are possible, unless told otherwise.
42 If the host has a `zic' command with a `-L leapsecondfilename' option,
43 then it supports leap seconds; otherwise it probably doesn't. */
44 #ifndef LEAP_SECONDS_POSSIBLE
45 # define LEAP_SECONDS_POSSIBLE 1
48 #include <sys/types.h> /* Some systems define `time_t' here. */
60 /* Make it work even if the system's libc has its own mktime routine. */
61 # define mktime my_mktime
65 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
66 # define __P(args) args
76 /* The extra casts work around common compiler bugs. */
77 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
78 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
79 It is necessary at least when t == time_t. */
80 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
81 ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
82 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
85 # define INT_MIN TYPE_MINIMUM (int)
88 # define INT_MAX TYPE_MAXIMUM (int)
92 # define TIME_T_MIN TYPE_MINIMUM (time_t)
95 # define TIME_T_MAX TYPE_MAXIMUM (time_t)
98 #define TM_YEAR_BASE 1900
99 #define EPOCH_YEAR 1970
102 /* Nonzero if YEAR is a leap year (every 4 years,
103 except every 100th isn't, and every 400th is). */
104 # define __isleap(year) \
105 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
108 /* How many days come before each month (0-12). */
109 const unsigned short int __mon_yday
[2][13] =
112 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
114 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
117 static struct tm
*ranged_convert
__P ((struct tm
*(*) __P ((const time_t *,
119 time_t *, struct tm
*));
120 static time_t ydhms_tm_diff
__P ((int, int, int, int, int, const struct tm
*));
121 time_t __mktime_internal
__P ((struct tm
*,
122 struct tm
*(*) (const time_t *, struct tm
*),
127 # define localtime_r __localtime_r
129 # if ! HAVE_LOCALTIME_R && ! defined localtime_r
130 /* Approximate localtime_r as best we can in its absence. */
131 # define localtime_r my_mktime_localtime_r
132 static struct tm
*localtime_r
__P ((const time_t *, struct tm
*));
138 struct tm
*l
= localtime (t
);
144 # endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
148 /* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
149 measured in seconds, ignoring leap seconds.
150 YEAR uses the same numbering as TM->tm_year.
151 All values are in range, except possibly YEAR.
152 If TP is null, return a nonzero value.
153 If overflow occurs, yield the low order bits of the correct answer. */
155 ydhms_tm_diff (year
, yday
, hour
, min
, sec
, tp
)
156 int year
, yday
, hour
, min
, sec
;
163 /* Compute intervening leap days correctly even if year is negative.
164 Take care to avoid int overflow. time_t overflow is OK, since
165 only the low order bits of the correct time_t answer are needed.
166 Don't convert to time_t until after all divisions are done, since
167 time_t might be unsigned. */
168 int a4
= (year
>> 2) + (TM_YEAR_BASE
>> 2) - ! (year
& 3);
169 int b4
= (tp
->tm_year
>> 2) + (TM_YEAR_BASE
>> 2) - ! (tp
->tm_year
& 3);
170 int a100
= a4
/ 25 - (a4
% 25 < 0);
171 int b100
= b4
/ 25 - (b4
% 25 < 0);
172 int a400
= a100
>> 2;
173 int b400
= b100
>> 2;
174 int intervening_leap_days
= (a4
- b4
) - (a100
- b100
) + (a400
- b400
);
175 time_t years
= year
- (time_t) tp
->tm_year
;
176 time_t days
= (365 * years
+ intervening_leap_days
177 + (yday
- tp
->tm_yday
));
178 return (60 * (60 * (24 * days
+ (hour
- tp
->tm_hour
))
179 + (min
- tp
->tm_min
))
180 + (sec
- tp
->tm_sec
));
185 static time_t localtime_offset
;
187 /* Convert *TP to a time_t value. */
193 /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
194 time zone names contained in the external variable `tzname' shall
195 be set as if the tzset() function had been called. */
199 return __mktime_internal (tp
, localtime_r
, &localtime_offset
);
202 /* Use CONVERT to convert *T to a broken down time in *TP.
203 If *T is out of range for conversion, adjust it so that
204 it is the nearest in-range value and then convert that. */
206 ranged_convert (convert
, t
, tp
)
207 struct tm
*(*convert
) __P ((const time_t *, struct tm
*));
213 if (! (r
= (*convert
) (t
, tp
)) && *t
)
219 /* BAD is a known unconvertible time_t, and OK is a known good one.
220 Use binary search to narrow the range between BAD and OK until
222 while (bad
!= ok
+ (bad
< 0 ? -1 : 1))
224 time_t mid
= *t
= (bad
< 0
225 ? bad
+ ((ok
- bad
) >> 1)
226 : ok
+ ((bad
- ok
) >> 1));
227 if ((r
= (*convert
) (t
, tp
)))
238 /* The last conversion attempt failed;
239 revert to the most recent successful attempt. */
250 /* Convert *TP to a time_t value, inverting
251 the monotonic and mostly-unit-linear conversion function CONVERT.
252 Use *OFFSET to keep track of a guess at the offset of the result,
253 compared to what the result would be for UTC without leap seconds.
254 If *OFFSET's guess is correct, only one CONVERT call is needed. */
256 __mktime_internal (tp
, convert
, offset
)
258 struct tm
*(*convert
) __P ((const time_t *, struct tm
*));
264 /* The maximum number of probes (calls to CONVERT) should be enough
265 to handle any combinations of time zone rule changes, solar time,
266 and leap seconds. POSIX.1 prohibits leap seconds, but some hosts
268 int remaining_probes
= 4;
270 /* Time requested. Copy it in case CONVERT modifies *TP; this can
271 occur if TP is localtime's returned value and CONVERT is localtime. */
272 int sec
= tp
->tm_sec
;
273 int min
= tp
->tm_min
;
274 int hour
= tp
->tm_hour
;
275 int mday
= tp
->tm_mday
;
276 int mon
= tp
->tm_mon
;
277 int year_requested
= tp
->tm_year
;
278 int isdst
= tp
->tm_isdst
;
280 /* Ensure that mon is in range, and set year accordingly. */
281 int mon_remainder
= mon
% 12;
282 int negative_mon_remainder
= mon_remainder
< 0;
283 int mon_years
= mon
/ 12 - negative_mon_remainder
;
284 int year
= year_requested
+ mon_years
;
286 /* The other values need not be in range:
287 the remaining code handles minor overflows correctly,
288 assuming int and time_t arithmetic wraps around.
289 Major overflows are caught at the end. */
291 /* Calculate day of year from year, month, and day of month.
292 The result need not be in range. */
293 int yday
= ((__mon_yday
[__isleap (year
+ TM_YEAR_BASE
)]
294 [mon_remainder
+ 12 * negative_mon_remainder
])
297 int sec_requested
= sec
;
298 #if LEAP_SECONDS_POSSIBLE
299 /* Handle out-of-range seconds specially,
300 since ydhms_tm_diff assumes every minute has 60 seconds. */
307 /* Invert CONVERT by probing. First assume the same offset as last time.
308 Then repeatedly use the error to improve the guess. */
310 tm
.tm_year
= EPOCH_YEAR
- TM_YEAR_BASE
;
311 tm
.tm_yday
= tm
.tm_hour
= tm
.tm_min
= tm
.tm_sec
= 0;
312 t0
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
, &tm
);
314 for (t
= t0
+ *offset
;
315 (dt
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
,
316 ranged_convert (convert
, &t
, &tm
)));
318 if (--remaining_probes
== 0)
321 /* Check whether tm.tm_isdst has the requested value, if any. */
322 if (0 <= isdst
&& 0 <= tm
.tm_isdst
)
324 int dst_diff
= (isdst
!= 0) - (tm
.tm_isdst
!= 0);
327 /* Move two hours in the direction indicated by the disagreement,
328 probe some more, and switch to a new time if found.
329 The largest known fallback due to daylight savings is two hours:
330 once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */
331 time_t ot
= t
- 2 * 60 * 60 * dst_diff
;
332 while (--remaining_probes
!= 0)
335 if (! (dt
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
,
336 ranged_convert (convert
, &ot
, &otm
))))
343 break; /* Avoid a redundant probe. */
350 #if LEAP_SECONDS_POSSIBLE
351 if (sec_requested
!= tm
.tm_sec
)
353 /* Adjust time to reflect the tm_sec requested, not the normalized value.
354 Also, repair any damage from a false match due to a leap second. */
355 t
+= sec_requested
- sec
+ (sec
== 0 && tm
.tm_sec
== 60);
356 if (! (*convert
) (&t
, &tm
))
361 if (TIME_T_MAX
/ INT_MAX
/ 366 / 24 / 60 / 60 < 3)
363 /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
364 so check for major overflows. A gross check suffices,
365 since if t has overflowed, it is off by a multiple of
366 TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of
367 the difference that is bounded by a small value. */
369 double dyear
= (double) year_requested
+ mon_years
- tm
.tm_year
;
370 double dday
= 366 * dyear
+ mday
;
371 double dsec
= 60 * (60 * (24 * dday
+ hour
) + min
) + sec_requested
;
373 /* On Irix4.0.5 cc, dividing TIME_T_MIN by 3 does not produce
374 correct results, ie., it erroneously gives a positive value
375 of 715827882. Setting a variable first then doing math on it
376 seems to work. (ghazi@caip.rutgers.edu) */
378 const time_t time_t_max
= TIME_T_MAX
;
379 const time_t time_t_min
= TIME_T_MIN
;
381 if (time_t_max
/ 3 - time_t_min
/ 3 < (dsec
< 0 ? - dsec
: dsec
))
390 weak_alias (mktime
, timelocal
)
400 return ((a
->tm_sec
^ b
->tm_sec
)
401 | (a
->tm_min
^ b
->tm_min
)
402 | (a
->tm_hour
^ b
->tm_hour
)
403 | (a
->tm_mday
^ b
->tm_mday
)
404 | (a
->tm_mon
^ b
->tm_mon
)
405 | (a
->tm_year
^ b
->tm_year
)
406 | (a
->tm_mday
^ b
->tm_mday
)
407 | (a
->tm_yday
^ b
->tm_yday
)
408 | (a
->tm_isdst
^ b
->tm_isdst
));
416 printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
417 tp
->tm_year
+ TM_YEAR_BASE
, tp
->tm_mon
+ 1, tp
->tm_mday
,
418 tp
->tm_hour
, tp
->tm_min
, tp
->tm_sec
,
419 tp
->tm_yday
, tp
->tm_wday
, tp
->tm_isdst
);
425 check_result (tk
, tmk
, tl
, lt
)
431 if (tk
!= tl
|| !lt
|| not_equal_tm (&tmk
, lt
))
435 printf (")\nyields (");
437 printf (") == %ld, should be %ld\n", (long) tl
, (long) tk
);
450 struct tm tm
, tmk
, tml
;
455 if ((argc
== 3 || argc
== 4)
456 && (sscanf (argv
[1], "%d-%d-%d%c",
457 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
, &trailer
)
459 && (sscanf (argv
[2], "%d:%d:%d%c",
460 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
, &trailer
)
463 tm
.tm_year
-= TM_YEAR_BASE
;
465 tm
.tm_isdst
= argc
== 3 ? -1 : atoi (argv
[3]);
468 lt
= localtime (&tl
);
474 printf ("mktime returns %ld == ", (long) tl
);
477 status
= check_result (tl
, tmk
, tl
, lt
);
479 else if (argc
== 4 || (argc
== 5 && strcmp (argv
[4], "-") == 0))
481 time_t from
= atol (argv
[1]);
482 time_t by
= atol (argv
[2]);
483 time_t to
= atol (argv
[3]);
486 for (tl
= from
; tl
<= to
; tl
+= by
)
488 lt
= localtime (&tl
);
493 status
|= check_result (tk
, tmk
, tl
, tml
);
497 printf ("localtime (%ld) yields 0\n", (long) tl
);
502 for (tl
= from
; tl
<= to
; tl
+= by
)
504 /* Null benchmark. */
505 lt
= localtime (&tl
);
510 status
|= check_result (tk
, tmk
, tl
, tml
);
514 printf ("localtime (%ld) yields 0\n", (long) tl
);
521 \t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
522 \t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
523 \t%s FROM BY TO - # Do not test those values (for benchmark).\n",
524 argv
[0], argv
[0], argv
[0]);
533 compile-command: "gcc -DDEBUG -D__EXTENSIONS__ -DHAVE_LIMITS_H -DHAVE_LOCALTIME_R -DSTDC_HEADERS -Wall -W -O -g mktime.c -o mktime"