1 /* Copyright (C) 1993, 1994, 1995, 1996, 1997 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@prep.ai.mit.edu.
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
31 # define HAVE_LIMITS_H 1
32 # define HAVE_LOCALTIME_R 1
33 # define STDC_HEADERS 1
36 /* Assume that leap seconds are possible, unless told otherwise.
37 If the host has a `zic' command with a `-L leapsecondfilename' option,
38 then it supports leap seconds; otherwise it probably doesn't. */
39 #ifndef LEAP_SECONDS_POSSIBLE
40 # define LEAP_SECONDS_POSSIBLE 1
43 #include <sys/types.h> /* Some systems define `time_t' here. */
55 /* Make it work even if the system's libc has its own mktime routine. */
56 # define mktime my_mktime
60 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
61 # define __P(args) args
72 # define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
75 # define INT_MAX (~0 - INT_MIN)
79 /* The outer cast to time_t works around a bug in Cray C 5.0.3.0. */
80 # define TIME_T_MIN ((time_t) \
81 (0 < (time_t) -1 ? (time_t) 0 \
82 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1)))
85 # define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
88 #define TM_YEAR_BASE 1900
89 #define EPOCH_YEAR 1970
92 /* Nonzero if YEAR is a leap year (every 4 years,
93 except every 100th isn't, and every 400th is). */
94 # define __isleap(year) \
95 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
98 /* How many days come before each month (0-12). */
99 const unsigned short int __mon_yday
[2][13] =
102 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
104 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
107 static time_t ydhms_tm_diff
__P ((int, int, int, int, int, const struct tm
*));
108 time_t __mktime_internal
__P ((struct tm
*,
109 struct tm
*(*) (const time_t *, struct tm
*),
114 # define localtime_r __localtime_r
116 # if ! HAVE_LOCALTIME_R && ! defined localtime_r
117 /* Approximate localtime_r as best we can in its absence. */
118 # define localtime_r my_mktime_localtime_r
119 static struct tm
*localtime_r
__P ((const time_t *, struct tm
*));
125 struct tm
*l
= localtime (t
);
131 # endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
135 /* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
136 measured in seconds, ignoring leap seconds.
137 YEAR uses the same numbering as TM->tm_year.
138 All values are in range, except possibly YEAR.
139 If overflow occurs, yield the low order bits of the correct answer. */
141 ydhms_tm_diff (year
, yday
, hour
, min
, sec
, tp
)
142 int year
, yday
, hour
, min
, sec
;
145 /* Compute intervening leap days correctly even if year is negative.
146 Take care to avoid int overflow. time_t overflow is OK, since
147 only the low order bits of the correct time_t answer are needed.
148 Don't convert to time_t until after all divisions are done, since
149 time_t might be unsigned. */
150 int a4
= (year
>> 2) + (TM_YEAR_BASE
>> 2) - ! (year
& 3);
151 int b4
= (tp
->tm_year
>> 2) + (TM_YEAR_BASE
>> 2) - ! (tp
->tm_year
& 3);
152 int a100
= a4
/ 25 - (a4
% 25 < 0);
153 int b100
= b4
/ 25 - (b4
% 25 < 0);
154 int a400
= a100
>> 2;
155 int b400
= b100
>> 2;
156 int intervening_leap_days
= (a4
- b4
) - (a100
- b100
) + (a400
- b400
);
157 time_t years
= year
- (time_t) tp
->tm_year
;
158 time_t days
= (365 * years
+ intervening_leap_days
159 + (yday
- tp
->tm_yday
));
160 return (60 * (60 * (24 * days
+ (hour
- tp
->tm_hour
))
161 + (min
- tp
->tm_min
))
162 + (sec
- tp
->tm_sec
));
166 static time_t localtime_offset
;
168 /* Convert *TP to a time_t value. */
174 /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
175 time zone names contained in the external variable `tzname' shall
176 be set as if the tzset() function had been called. */
180 return __mktime_internal (tp
, localtime_r
, &localtime_offset
);
183 /* Convert *TP to a time_t value, inverting
184 the monotonic and mostly-unit-linear conversion function CONVERT.
185 Use *OFFSET to keep track of a guess at the offset of the result,
186 compared to what the result would be for UTC without leap seconds.
187 If *OFFSET's guess is correct, only one CONVERT call is needed. */
189 __mktime_internal (tp
, convert
, offset
)
191 struct tm
*(*convert
) __P ((const time_t *, struct tm
*));
197 /* The maximum number of probes (calls to CONVERT) should be enough
198 to handle any combinations of time zone rule changes, solar time,
199 and leap seconds. Posix.1 prohibits leap seconds, but some hosts
201 int remaining_probes
= 4;
203 /* Time requested. Copy it in case CONVERT modifies *TP; this can
204 occur if TP is localtime's returned value and CONVERT is localtime. */
205 int sec
= tp
->tm_sec
;
206 int min
= tp
->tm_min
;
207 int hour
= tp
->tm_hour
;
208 int mday
= tp
->tm_mday
;
209 int mon
= tp
->tm_mon
;
210 int year_requested
= tp
->tm_year
;
211 int isdst
= tp
->tm_isdst
;
213 /* Ensure that mon is in range, and set year accordingly. */
214 int mon_remainder
= mon
% 12;
215 int negative_mon_remainder
= mon_remainder
< 0;
216 int mon_years
= mon
/ 12 - negative_mon_remainder
;
217 int year
= year_requested
+ mon_years
;
219 /* The other values need not be in range:
220 the remaining code handles minor overflows correctly,
221 assuming int and time_t arithmetic wraps around.
222 Major overflows are caught at the end. */
224 /* Calculate day of year from year, month, and day of month.
225 The result need not be in range. */
226 int yday
= ((__mon_yday
[__isleap (year
+ TM_YEAR_BASE
)]
227 [mon_remainder
+ 12 * negative_mon_remainder
])
230 #if LEAP_SECONDS_POSSIBLE
231 /* Handle out-of-range seconds specially,
232 since ydhms_tm_diff assumes every minute has 60 seconds. */
233 int sec_requested
= sec
;
240 /* Invert CONVERT by probing. First assume the same offset as last time.
241 Then repeatedly use the error to improve the guess. */
243 tm
.tm_year
= EPOCH_YEAR
- TM_YEAR_BASE
;
244 tm
.tm_yday
= tm
.tm_hour
= tm
.tm_min
= tm
.tm_sec
= 0;
245 t0
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
, &tm
);
247 for (t
= t0
+ *offset
;
248 (dt
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
, (*convert
) (&t
, &tm
)));
250 if (--remaining_probes
== 0)
253 /* Check whether tm.tm_isdst has the requested value, if any. */
254 if (0 <= isdst
&& 0 <= tm
.tm_isdst
)
256 int dst_diff
= (isdst
!= 0) - (tm
.tm_isdst
!= 0);
259 /* Move two hours in the direction indicated by the disagreement,
260 probe some more, and switch to a new time if found.
261 The largest known fallback due to daylight savings is two hours:
262 once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */
263 time_t ot
= t
- 2 * 60 * 60 * dst_diff
;
264 while (--remaining_probes
!= 0)
267 if (! (dt
= ydhms_tm_diff (year
, yday
, hour
, min
, sec
,
268 (*convert
) (&ot
, &otm
))))
275 break; /* Avoid a redundant probe. */
282 #if LEAP_SECONDS_POSSIBLE
283 if (sec_requested
!= tm
.tm_sec
)
285 /* Adjust time to reflect the tm_sec requested, not the normalized value.
286 Also, repair any damage from a false match due to a leap second. */
287 t
+= sec_requested
- sec
+ (sec
== 0 && tm
.tm_sec
== 60);
288 (*convert
) (&t
, &tm
);
292 if (TIME_T_MAX
/ INT_MAX
/ 366 / 24 / 60 / 60 < 3)
294 /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
295 so check for major overflows. A gross check suffices,
296 since if t has overflowed, it is off by a multiple of
297 TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of
298 the difference that is bounded by a small value. */
300 double dyear
= (double) year_requested
+ mon_years
- tm
.tm_year
;
301 double dday
= 366 * dyear
+ mday
;
302 double dsec
= 60 * (60 * (24 * dday
+ hour
) + min
) + sec_requested
;
304 if (TIME_T_MAX
/ 3 - TIME_T_MIN
/ 3 < (dsec
< 0 ? - dsec
: dsec
))
313 weak_alias (mktime
, timelocal
)
323 return ((a
->tm_sec
^ b
->tm_sec
)
324 | (a
->tm_min
^ b
->tm_min
)
325 | (a
->tm_hour
^ b
->tm_hour
)
326 | (a
->tm_mday
^ b
->tm_mday
)
327 | (a
->tm_mon
^ b
->tm_mon
)
328 | (a
->tm_year
^ b
->tm_year
)
329 | (a
->tm_mday
^ b
->tm_mday
)
330 | (a
->tm_yday
^ b
->tm_yday
)
331 | (a
->tm_isdst
^ b
->tm_isdst
));
338 printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
339 tp
->tm_year
+ TM_YEAR_BASE
, tp
->tm_mon
+ 1, tp
->tm_mday
,
340 tp
->tm_hour
, tp
->tm_min
, tp
->tm_sec
,
341 tp
->tm_yday
, tp
->tm_wday
, tp
->tm_isdst
);
345 check_result (tk
, tmk
, tl
, tml
)
351 if (tk
!= tl
|| not_equal_tm (&tmk
, &tml
))
355 printf (")\nyields (");
357 printf (") == %ld, should be %ld\n", (long) tl
, (long) tk
);
370 struct tm tm
, tmk
, tml
;
374 if ((argc
== 3 || argc
== 4)
375 && (sscanf (argv
[1], "%d-%d-%d%c",
376 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
, &trailer
)
378 && (sscanf (argv
[2], "%d:%d:%d%c",
379 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
, &trailer
)
382 tm
.tm_year
-= TM_YEAR_BASE
;
384 tm
.tm_isdst
= argc
== 3 ? -1 : atoi (argv
[3]);
387 tml
= *localtime (&tl
);
388 printf ("mktime returns %ld == ", (long) tl
);
391 status
= check_result (tl
, tmk
, tl
, tml
);
393 else if (argc
== 4 || (argc
== 5 && strcmp (argv
[4], "-") == 0))
395 time_t from
= atol (argv
[1]);
396 time_t by
= atol (argv
[2]);
397 time_t to
= atol (argv
[3]);
400 for (tl
= from
; tl
<= to
; tl
+= by
)
402 tml
= *localtime (&tl
);
405 status
|= check_result (tk
, tmk
, tl
, tml
);
408 for (tl
= from
; tl
<= to
; tl
+= by
)
410 /* Null benchmark. */
411 tml
= *localtime (&tl
);
414 status
|= check_result (tk
, tmk
, tl
, tml
);
419 \t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
420 \t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
421 \t%s FROM BY TO - # Do not test those values (for benchmark).\n",
422 argv
[0], argv
[0], argv
[0]);
431 compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"