1 /* Copyright (C) 1993, 1994, 1995 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
5 Library. 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 Foundation,
19 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 static 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 static 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 /* Convert *TP to a time_t value. */
154 static time_t localtime_offset;
155 return __mktime_internal (tp, localtime_r, &localtime_offset);
158 /* Convert *TP to a time_t value, inverting
159 the monotonic and mostly-unit-linear conversion function CONVERT.
160 Use *OFFSET to keep track of a guess at the offset of the result,
161 compared to what the result would be for UTC without leap seconds.
162 If *OFFSET's guess is correct, only one CONVERT call is needed. */
164 __mktime_internal (tp, convert, offset)
166 struct tm *(*convert) __P ((const time_t *, struct tm *));
172 /* The maximum number of probes (calls to CONVERT) should be enough
173 to handle any combinations of time zone rule changes, solar time,
174 and leap seconds. Posix.1 prohibits leap seconds, but some hosts
176 int remaining_probes = 4;
178 /* Time requested. Copy it in case CONVERT modifies *TP; this can
179 occur if TP is localtime's returned value and CONVERT is localtime. */
180 int sec = tp->tm_sec;
181 int min = tp->tm_min;
182 int hour = tp->tm_hour;
183 int mday = tp->tm_mday;
184 int mon = tp->tm_mon;
185 int year_requested = tp->tm_year;
186 int isdst = tp->tm_isdst;
188 /* Ensure that mon is in range, and set year accordingly. */
189 int mon_remainder = mon % 12;
190 int negative_mon_remainder = mon_remainder < 0;
191 int mon_years = mon / 12 - negative_mon_remainder;
192 int year = year_requested + mon_years;
194 /* The other values need not be in range:
195 the remaining code handles minor overflows correctly,
196 assuming int and time_t arithmetic wraps around.
197 Major overflows are caught at the end. */
199 /* Calculate day of year from year, month, and day of month.
200 The result need not be in range. */
201 int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
202 [mon_remainder + 12 * negative_mon_remainder])
205 #if LEAP_SECONDS_POSSIBLE
206 /* Handle out-of-range seconds specially,
207 since ydhms_tm_diff assumes every minute has 60 seconds. */
208 int sec_requested = sec;
215 /* Invert CONVERT by probing. First assume the same offset as last time.
216 Then repeatedly use the error to improve the guess. */
218 tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
219 tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
220 t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);
222 for (t = t0 + *offset;
223 (dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm)));
225 if (--remaining_probes == 0)
228 /* Check whether tm.tm_isdst has the requested value, if any. */
229 if (0 <= isdst && 0 <= tm.tm_isdst)
231 int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
234 /* Move two hours in the direction indicated by the disagreement,
235 probe some more, and switch to a new time if found.
236 The largest known fallback due to daylight savings is two hours:
237 once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */
238 time_t ot = t - 2 * 60 * 60 * dst_diff;
239 while (--remaining_probes != 0)
242 if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
243 (*convert) (&ot, &otm))))
250 break; /* Avoid a redundant probe. */
257 #if LEAP_SECONDS_POSSIBLE
258 if (sec_requested != tm.tm_sec)
260 /* Adjust time to reflect the tm_sec requested, not the normalized value.
261 Also, repair any damage from a false match due to a leap second. */
262 t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
263 (*convert) (&t, &tm);
267 if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
269 /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
270 so check for major overflows. A gross check suffices,
271 since if t has overflowed, it is off by a multiple of
272 TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of
273 the difference that is bounded by a small value. */
275 double dyear = (double) year_requested + mon_years - tm.tm_year;
276 double dday = 366 * dyear + mday;
277 double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
279 if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec))
288 weak_alias (mktime, timelocal)
298 return ((a->tm_sec ^ b->tm_sec)
299 | (a->tm_min ^ b->tm_min)
300 | (a->tm_hour ^ b->tm_hour)
301 | (a->tm_mday ^ b->tm_mday)
302 | (a->tm_mon ^ b->tm_mon)
303 | (a->tm_year ^ b->tm_year)
304 | (a->tm_mday ^ b->tm_mday)
305 | (a->tm_yday ^ b->tm_yday)
306 | (a->tm_isdst ^ b->tm_isdst));
313 printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
314 tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
315 tp->tm_hour, tp->tm_min, tp->tm_sec,
316 tp->tm_yday, tp->tm_wday, tp->tm_isdst);
320 check_result (tk, tmk, tl, tml)
326 if (tk != tl || not_equal_tm (&tmk, &tml))
330 printf (")\nyields (");
332 printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
345 struct tm tm, tmk, tml;
349 if ((argc == 3 || argc == 4)
350 && (sscanf (argv[1], "%d-%d-%d%c",
351 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
353 && (sscanf (argv[2], "%d:%d:%d%c",
354 &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
357 tm.tm_year -= TM_YEAR_BASE;
359 tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
362 tml = *localtime (&tl);
363 printf ("mktime returns %ld == ", (long) tl);
366 status = check_result (tl, tmk, tl, tml);
368 else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
370 time_t from = atol (argv[1]);
371 time_t by = atol (argv[2]);
372 time_t to = atol (argv[3]);
375 for (tl = from; tl <= to; tl += by)
377 tml = *localtime (&tl);
380 status |= check_result (tk, tmk, tl, tml);
383 for (tl = from; tl <= to; tl += by)
385 /* Null benchmark. */
386 tml = *localtime (&tl);
389 status |= check_result (tk, tmk, tl, tml);
394 \t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
395 \t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
396 \t%s FROM BY TO - # Do not test those values (for benchmark).\n",
397 argv[0], argv[0], argv[0]);
406 compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"