1 /* Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
2 Contributed by Paul Eggert (eggert@twinsun.com).
4 This file is part of the GNU C Library.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If
18 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
19 Cambridge, MA 02139, USA. */
21 /* Define this to have a standalone program to test this implementation of
29 /* Assume that leap seconds are possible, unless told otherwise.
30 If the host has a `zic' command with a `-L leapsecondfilename' option,
31 then it supports leap seconds; otherwise it probably doesn't. */
32 #ifndef LEAP_SECONDS_POSSIBLE
33 #define LEAP_SECONDS_POSSIBLE 1
36 #include <sys/types.h> /* Some systems define `time_t' here. */
39 #if __STDC__ || __GNU_LIBRARY__ || STDC_HEADERS
45 #if __STDC__ || __GNU_LIBRARY__ || STDC_HEADERS
48 /* Make it work even if the system's libc has its own mktime routine. */
49 #define mktime my_mktime
53 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
54 #define __P(args) args
65 #define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
68 #define INT_MAX (~0 - INT_MIN)
72 #define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \
73 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
76 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
79 #define TM_YEAR_BASE 1900
80 #define EPOCH_YEAR 1970
83 /* Nonzero if YEAR is a leap year (every 4 years,
84 except every 100th isn't, and every 400th is). */
85 #define __isleap(year) \
86 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
89 /* How many days come before each month (0-12). */
90 const unsigned short int __mon_yday
[2][13] =
93 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
95 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
98 static time_t ydhms_tm_diff
__P ((int, int, int, int, int, const struct tm
*));
99 time_t __mktime_internal
__P ((struct tm
*,
100 struct tm
*(*) (const time_t *, struct tm
*),
104 #if ! HAVE_LOCALTIME_R && ! defined (localtime_r)
106 #define localtime_r __localtime_r
108 /* Approximate localtime_r as best we can in its absence. */
109 #define localtime_r my_localtime_r
110 static struct tm
*localtime_r
__P ((const time_t *, struct tm
*));
116 struct tm
*l
= localtime (t
);
123 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
126 /* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
127 measured in seconds, ignoring leap seconds.
128 YEAR uses the same numbering as TM->tm_year.
129 All values are in range, except possibly YEAR.
130 If overflow occurs, yield the low order bits of the correct answer. */
132 ydhms_tm_diff (year
, yday
, hour
, min
, sec
, tp
)
133 int year
, yday
, hour
, min
, sec
;
136 time_t ay
= year
+ (time_t) (TM_YEAR_BASE
- 1);
137 time_t by
= tp
->tm_year
+ (time_t) (TM_YEAR_BASE
- 1);
138 time_t intervening_leap_days
=
139 (ay
/4 - by
/4) - (ay
/100 - by
/100) + (ay
/400 - by
/400);
140 time_t years
= ay
- by
;
141 time_t days
= (365 * years
+ intervening_leap_days
142 + (yday
- tp
->tm_yday
));
143 return (60 * (60 * (24 * days
+ (hour
- tp
->tm_hour
))
144 + (min
- tp
->tm_min
))
145 + (sec
- tp
->tm_sec
));
149 static time_t localtime_offset
;
151 /* Convert *TP to a time_t value. */
156 return __mktime_internal (tp
, localtime_r
, &localtime_offset
);
159 /* Convert *TP to a time_t value, inverting
160 the monotonic and mostly-unit-linear conversion function CONVERT.
161 Use *OFFSET to keep track of a guess at the offset of the result,
162 compared to what the result would be for UTC without leap seconds.
163 If *OFFSET's guess is correct, only one CONVERT call is needed. */
165 __mktime_internal (tp
, convert
, offset
)
167 struct tm
*(*convert
) __P ((const time_t *, struct tm
*));
173 /* The maximum number of probes (calls to CONVERT) should be enough
174 to handle any combinations of time zone rule changes, solar time,
175 and leap seconds. Posix.1 prohibits leap seconds, but some hosts
177 int remaining_probes
= 4;
179 /* Time requested. Copy it in case CONVERT modifies *TP; this can
180 occur if TP is localtime's returned value and CONVERT is localtime. */
181 int sec
= tp
->tm_sec
;
182 int min
= tp
->tm_min
;
183 int hour
= tp
->tm_hour
;
184 int mday
= tp
->tm_mday
;
185 int mon
= tp
->tm_mon
;
186 int year_requested
= tp
->tm_year
;
187 int isdst
= tp
->tm_isdst
;
189 /* Ensure that mon is in range, and set year accordingly. */
190 int mon_remainder
= mon
% 12;
191 int negative_mon_remainder
= mon_remainder
< 0;
192 int mon_years
= mon
/ 12 - negative_mon_remainder
;
193 int year
= year_requested
+ mon_years
;
195 /* The other values need not be in range:
196 the remaining code handles minor overflows correctly,
197 assuming int and time_t arithmetic wraps around.
198 Major overflows are caught at the end. */
200 /* Calculate day of year from year, month, and day of month.
201 The result need not be in range. */
202 int yday
= ((__mon_yday
[__isleap (year
+ TM_YEAR_BASE
)]
203 [mon_remainder
+ 12 * negative_mon_remainder
])
206 #if LEAP_SECONDS_POSSIBLE
207 /* Handle out-of-range seconds specially,
208 since ydhms_tm_diff assumes every minute has 60 seconds. */
209 int sec_requested
= sec
;
216 /* Invert CONVERT by probing. First assume the same offset as last time.
217 Then repeatedly use the error to improve the guess. */
219 tm
.tm_year
= EPOCH_YEAR
- TM_YEAR_BASE
;
220 tm
.tm_yday
= tm
.tm_hour
= tm
.tm_min
= tm
.tm_sec
= 0;
221 t0
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
, &tm
);
223 for (t
= t0
+ *offset
;
224 (dt
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
, (*convert
) (&t
, &tm
)));
226 if (--remaining_probes
== 0)
229 /* Check whether tm.tm_isdst has the requested value, if any. */
230 if (0 <= isdst
&& 0 <= tm
.tm_isdst
)
232 int dst_diff
= (isdst
!= 0) - (tm
.tm_isdst
!= 0);
235 /* Move two hours in the direction indicated by the disagreement,
236 probe some more, and switch to a new time if found.
237 The largest known fallback due to daylight savings is two hours:
238 once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */
239 time_t ot
= t
- 2 * 60 * 60 * dst_diff
;
240 while (--remaining_probes
!= 0)
243 if (! (dt
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
,
244 (*convert
) (&ot
, &otm
))))
251 break; /* Avoid a redundant probe. */
258 #if LEAP_SECONDS_POSSIBLE
259 if (sec_requested
!= tm
.tm_sec
)
261 /* Adjust time to reflect the tm_sec requested, not the normalized value.
262 Also, repair any damage from a false match due to a leap second. */
263 t
+= sec_requested
- sec
+ (sec
== 0 && tm
.tm_sec
== 60);
264 (*convert
) (&t
, &tm
);
268 if (TIME_T_MAX
/ INT_MAX
/ 366 / 24 / 60 / 60 < 3)
270 /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
271 so check for major overflows. A gross check suffices,
272 since if t has overflowed, it is off by a multiple of
273 TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of
274 the difference that is bounded by a small value. */
276 double dyear
= (double) year_requested
+ mon_years
- tm
.tm_year
;
277 double dday
= 366 * dyear
+ mday
;
278 double dsec
= 60 * (60 * (24 * dday
+ hour
) + min
) + sec_requested
;
280 if (TIME_T_MAX
/ 3 - TIME_T_MIN
/ 3 < (dsec
< 0 ? - dsec
: dsec
))
289 weak_alias (mktime
, timelocal
)
299 return ((a
->tm_sec
^ b
->tm_sec
)
300 | (a
->tm_min
^ b
->tm_min
)
301 | (a
->tm_hour
^ b
->tm_hour
)
302 | (a
->tm_mday
^ b
->tm_mday
)
303 | (a
->tm_mon
^ b
->tm_mon
)
304 | (a
->tm_year
^ b
->tm_year
)
305 | (a
->tm_mday
^ b
->tm_mday
)
306 | (a
->tm_yday
^ b
->tm_yday
)
307 | (a
->tm_isdst
^ b
->tm_isdst
));
314 printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
315 tp
->tm_year
+ TM_YEAR_BASE
, tp
->tm_mon
+ 1, tp
->tm_mday
,
316 tp
->tm_hour
, tp
->tm_min
, tp
->tm_sec
,
317 tp
->tm_yday
, tp
->tm_wday
, tp
->tm_isdst
);
321 check_result (tk
, tmk
, tl
, tml
)
327 if (tk
!= tl
|| not_equal_tm (&tmk
, &tml
))
331 printf (")\nyields (");
333 printf (") == %ld, should be %ld\n", (long) tl
, (long) tk
);
346 struct tm tm
, tmk
, tml
;
350 if ((argc
== 3 || argc
== 4)
351 && (sscanf (argv
[1], "%d-%d-%d%c",
352 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
, &trailer
)
354 && (sscanf (argv
[2], "%d:%d:%d%c",
355 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
, &trailer
)
358 tm
.tm_year
-= TM_YEAR_BASE
;
360 tm
.tm_isdst
= argc
== 3 ? -1 : atoi (argv
[3]);
363 tml
= *localtime (&tl
);
364 printf ("mktime returns %ld == ", (long) tl
);
367 status
= check_result (tl
, tmk
, tl
, tml
);
369 else if (argc
== 4 || (argc
== 5 && strcmp (argv
[4], "-") == 0))
371 time_t from
= atol (argv
[1]);
372 time_t by
= atol (argv
[2]);
373 time_t to
= atol (argv
[3]);
376 for (tl
= from
; tl
<= to
; tl
+= by
)
378 tml
= *localtime (&tl
);
381 status
|= check_result (tk
, tmk
, tl
, tml
);
384 for (tl
= from
; tl
<= to
; tl
+= by
)
386 /* Null benchmark. */
387 tml
= *localtime (&tl
);
390 status
|= check_result (tk
, tmk
, tl
, tml
);
395 \t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
396 \t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
397 \t%s FROM BY TO - # Do not test those values (for benchmark).\n",
398 argv
[0], argv
[0], argv
[0]);
407 compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"