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
28 /* Assume that leap seconds are possible, unless told otherwise.
29 If the host has a `zic' command with a `-L leapsecondfilename' option,
30 then it supports leap seconds; otherwise it probably doesn't. */
31 #ifndef LEAP_SECONDS_POSSIBLE
32 #define LEAP_SECONDS_POSSIBLE 1
35 #include <sys/types.h> /* Some systems define `time_t' here. */
38 #if __STDC__ || __GNU_LIBRARY__ || STDC_HEADERS
44 #if __STDC__ || __GNU_LIBRARY__ || STDC_HEADERS
47 /* Make it work even if the system's libc has its own mktime routine. */
48 #define mktime my_mktime
52 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
53 #define __P(args) args
64 #define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
67 #define INT_MAX (~0 - INT_MIN)
71 #define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \
72 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
75 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
78 #define TM_YEAR_BASE 1900
79 #define EPOCH_YEAR 1970
82 /* Nonzero if YEAR is a leap year (every 4 years,
83 except every 100th isn't, and every 400th is). */
84 #define __isleap(year) \
85 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
88 /* How many days come before each month (0-12). */
89 const unsigned short int __mon_yday
[2][13] =
92 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
94 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
97 static time_t ydhms_tm_diff
__P ((int, int, int, int, int, const struct tm
*));
98 time_t __mktime_internal
__P ((struct tm
*,
99 struct tm
*(*) (const time_t *, struct tm
*),
103 #if ! HAVE_LOCALTIME_R && ! defined (localtime_r)
105 #define localtime_r __localtime_r
107 /* Approximate localtime_r as best we can in its absence. */
108 #define localtime_r my_localtime_r
109 static struct tm
*localtime_r
__P ((const time_t *, struct tm
*));
115 struct tm
*l
= localtime (t
);
122 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
125 /* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
126 measured in seconds, ignoring leap seconds.
127 YEAR uses the same numbering as TM->tm_year.
128 All values are in range, except possibly YEAR.
129 If overflow occurs, yield the low order bits of the correct answer. */
131 ydhms_tm_diff (year
, yday
, hour
, min
, sec
, tp
)
132 int year
, yday
, hour
, min
, sec
;
135 /* Compute intervening leap days correctly even if year is negative.
136 Take care to avoid int overflow. time_t overflow is OK, since
137 only the low order bits of the correct time_t answer are needed.
138 Don't convert to time_t until after all divisions are done, since
139 time_t might be unsigned. */
140 int a4
= (year
>> 2) + (TM_YEAR_BASE
>> 2) - ! (year
& 3);
141 int b4
= (tp
->tm_year
>> 2) + (TM_YEAR_BASE
>> 2) - ! (tp
->tm_year
& 3);
142 int a100
= a4
/ 25 - (a4
% 25 < 0);
143 int b100
= b4
/ 25 - (b4
% 25 < 0);
144 int a400
= a100
>> 2;
145 int b400
= b100
>> 2;
146 int intervening_leap_days
= (a4
- b4
) - (a100
- b100
) + (a400
- b400
);
147 time_t years
= year
- (time_t) tp
->tm_year
;
148 time_t days
= (365 * years
+ intervening_leap_days
149 + (yday
- tp
->tm_yday
));
150 return (60 * (60 * (24 * days
+ (hour
- tp
->tm_hour
))
151 + (min
- tp
->tm_min
))
152 + (sec
- tp
->tm_sec
));
156 static time_t localtime_offset
;
158 /* Convert *TP to a time_t value. */
164 /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
165 time zone names contained in the external variable `tzname' shall
166 be set as if the tzset() function had been called. */
170 return __mktime_internal (tp
, localtime_r
, &localtime_offset
);
173 /* Convert *TP to a time_t value, inverting
174 the monotonic and mostly-unit-linear conversion function CONVERT.
175 Use *OFFSET to keep track of a guess at the offset of the result,
176 compared to what the result would be for UTC without leap seconds.
177 If *OFFSET's guess is correct, only one CONVERT call is needed. */
179 __mktime_internal (tp
, convert
, offset
)
181 struct tm
*(*convert
) __P ((const time_t *, struct tm
*));
187 /* The maximum number of probes (calls to CONVERT) should be enough
188 to handle any combinations of time zone rule changes, solar time,
189 and leap seconds. Posix.1 prohibits leap seconds, but some hosts
191 int remaining_probes
= 4;
193 /* Time requested. Copy it in case CONVERT modifies *TP; this can
194 occur if TP is localtime's returned value and CONVERT is localtime. */
195 int sec
= tp
->tm_sec
;
196 int min
= tp
->tm_min
;
197 int hour
= tp
->tm_hour
;
198 int mday
= tp
->tm_mday
;
199 int mon
= tp
->tm_mon
;
200 int year_requested
= tp
->tm_year
;
201 int isdst
= tp
->tm_isdst
;
203 /* Ensure that mon is in range, and set year accordingly. */
204 int mon_remainder
= mon
% 12;
205 int negative_mon_remainder
= mon_remainder
< 0;
206 int mon_years
= mon
/ 12 - negative_mon_remainder
;
207 int year
= year_requested
+ mon_years
;
209 /* The other values need not be in range:
210 the remaining code handles minor overflows correctly,
211 assuming int and time_t arithmetic wraps around.
212 Major overflows are caught at the end. */
214 /* Calculate day of year from year, month, and day of month.
215 The result need not be in range. */
216 int yday
= ((__mon_yday
[__isleap (year
+ TM_YEAR_BASE
)]
217 [mon_remainder
+ 12 * negative_mon_remainder
])
220 #if LEAP_SECONDS_POSSIBLE
221 /* Handle out-of-range seconds specially,
222 since ydhms_tm_diff assumes every minute has 60 seconds. */
223 int sec_requested
= sec
;
230 /* Invert CONVERT by probing. First assume the same offset as last time.
231 Then repeatedly use the error to improve the guess. */
233 tm
.tm_year
= EPOCH_YEAR
- TM_YEAR_BASE
;
234 tm
.tm_yday
= tm
.tm_hour
= tm
.tm_min
= tm
.tm_sec
= 0;
235 t0
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
, &tm
);
237 for (t
= t0
+ *offset
;
238 (dt
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
, (*convert
) (&t
, &tm
)));
240 if (--remaining_probes
== 0)
243 /* Check whether tm.tm_isdst has the requested value, if any. */
244 if (0 <= isdst
&& 0 <= tm
.tm_isdst
)
246 int dst_diff
= (isdst
!= 0) - (tm
.tm_isdst
!= 0);
249 /* Move two hours in the direction indicated by the disagreement,
250 probe some more, and switch to a new time if found.
251 The largest known fallback due to daylight savings is two hours:
252 once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */
253 time_t ot
= t
- 2 * 60 * 60 * dst_diff
;
254 while (--remaining_probes
!= 0)
257 if (! (dt
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
,
258 (*convert
) (&ot
, &otm
))))
265 break; /* Avoid a redundant probe. */
272 #if LEAP_SECONDS_POSSIBLE
273 if (sec_requested
!= tm
.tm_sec
)
275 /* Adjust time to reflect the tm_sec requested, not the normalized value.
276 Also, repair any damage from a false match due to a leap second. */
277 t
+= sec_requested
- sec
+ (sec
== 0 && tm
.tm_sec
== 60);
278 (*convert
) (&t
, &tm
);
282 if (TIME_T_MAX
/ INT_MAX
/ 366 / 24 / 60 / 60 < 3)
284 /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
285 so check for major overflows. A gross check suffices,
286 since if t has overflowed, it is off by a multiple of
287 TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of
288 the difference that is bounded by a small value. */
290 double dyear
= (double) year_requested
+ mon_years
- tm
.tm_year
;
291 double dday
= 366 * dyear
+ mday
;
292 double dsec
= 60 * (60 * (24 * dday
+ hour
) + min
) + sec_requested
;
294 if (TIME_T_MAX
/ 3 - TIME_T_MIN
/ 3 < (dsec
< 0 ? - dsec
: dsec
))
303 weak_alias (mktime
, timelocal
)
313 return ((a
->tm_sec
^ b
->tm_sec
)
314 | (a
->tm_min
^ b
->tm_min
)
315 | (a
->tm_hour
^ b
->tm_hour
)
316 | (a
->tm_mday
^ b
->tm_mday
)
317 | (a
->tm_mon
^ b
->tm_mon
)
318 | (a
->tm_year
^ b
->tm_year
)
319 | (a
->tm_mday
^ b
->tm_mday
)
320 | (a
->tm_yday
^ b
->tm_yday
)
321 | (a
->tm_isdst
^ b
->tm_isdst
));
328 printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
329 tp
->tm_year
+ TM_YEAR_BASE
, tp
->tm_mon
+ 1, tp
->tm_mday
,
330 tp
->tm_hour
, tp
->tm_min
, tp
->tm_sec
,
331 tp
->tm_yday
, tp
->tm_wday
, tp
->tm_isdst
);
335 check_result (tk
, tmk
, tl
, tml
)
341 if (tk
!= tl
|| not_equal_tm (&tmk
, &tml
))
345 printf (")\nyields (");
347 printf (") == %ld, should be %ld\n", (long) tl
, (long) tk
);
360 struct tm tm
, tmk
, tml
;
364 if ((argc
== 3 || argc
== 4)
365 && (sscanf (argv
[1], "%d-%d-%d%c",
366 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
, &trailer
)
368 && (sscanf (argv
[2], "%d:%d:%d%c",
369 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
, &trailer
)
372 tm
.tm_year
-= TM_YEAR_BASE
;
374 tm
.tm_isdst
= argc
== 3 ? -1 : atoi (argv
[3]);
377 tml
= *localtime (&tl
);
378 printf ("mktime returns %ld == ", (long) tl
);
381 status
= check_result (tl
, tmk
, tl
, tml
);
383 else if (argc
== 4 || (argc
== 5 && strcmp (argv
[4], "-") == 0))
385 time_t from
= atol (argv
[1]);
386 time_t by
= atol (argv
[2]);
387 time_t to
= atol (argv
[3]);
390 for (tl
= from
; tl
<= to
; tl
+= by
)
392 tml
= *localtime (&tl
);
395 status
|= check_result (tk
, tmk
, tl
, tml
);
398 for (tl
= from
; tl
<= to
; tl
+= by
)
400 /* Null benchmark. */
401 tml
= *localtime (&tl
);
404 status
|= check_result (tk
, tmk
, tl
, tml
);
409 \t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
410 \t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
411 \t%s FROM BY TO - # Do not test those values (for benchmark).\n",
412 argv
[0], argv
[0], argv
[0]);
421 compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"