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
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
30 # define HAVE_LIMITS_H 1
31 # define HAVE_LOCALTIME_R 1
32 # define STDC_HEADERS 1
35 /* Assume that leap seconds are possible, unless told otherwise.
36 If the host has a `zic' command with a `-L leapsecondfilename' option,
37 then it supports leap seconds; otherwise it probably doesn't. */
38 #ifndef LEAP_SECONDS_POSSIBLE
39 #define LEAP_SECONDS_POSSIBLE 1
42 #include <sys/types.h> /* Some systems define `time_t' here. */
54 /* Make it work even if the system's libc has its own mktime routine. */
55 #define mktime my_mktime
59 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
60 #define __P(args) args
71 #define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
74 #define INT_MAX (~0 - INT_MIN)
78 /* The outer cast to time_t works around a bug in Cray C 5.0.3.0. */
79 #define TIME_T_MIN ((time_t) \
80 (0 < (time_t) -1 ? (time_t) 0 \
81 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1)))
84 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
87 #define TM_YEAR_BASE 1900
88 #define EPOCH_YEAR 1970
91 /* Nonzero if YEAR is a leap year (every 4 years,
92 except every 100th isn't, and every 400th is). */
93 #define __isleap(year) \
94 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
97 /* How many days come before each month (0-12). */
98 const unsigned short int __mon_yday[2][13] =
101 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
103 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
106 static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *));
107 time_t __mktime_internal __P ((struct tm *,
108 struct tm *(*) (const time_t *, struct tm *),
113 #define localtime_r __localtime_r
115 #if ! HAVE_LOCALTIME_R && ! defined (localtime_r)
116 /* Approximate localtime_r as best we can in its absence. */
117 #define localtime_r my_localtime_r
118 static struct tm *localtime_r __P ((const time_t *, struct tm *));
124 struct tm *l = localtime (t);
130 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
134 /* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
135 measured in seconds, ignoring leap seconds.
136 YEAR uses the same numbering as TM->tm_year.
137 All values are in range, except possibly YEAR.
138 If overflow occurs, yield the low order bits of the correct answer. */
140 ydhms_tm_diff (year, yday, hour, min, sec, tp)
141 int year, yday, hour, min, sec;
144 /* Compute intervening leap days correctly even if year is negative.
145 Take care to avoid int overflow. time_t overflow is OK, since
146 only the low order bits of the correct time_t answer are needed.
147 Don't convert to time_t until after all divisions are done, since
148 time_t might be unsigned. */
149 int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
150 int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
151 int a100 = a4 / 25 - (a4 % 25 < 0);
152 int b100 = b4 / 25 - (b4 % 25 < 0);
153 int a400 = a100 >> 2;
154 int b400 = b100 >> 2;
155 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
156 time_t years = year - (time_t) tp->tm_year;
157 time_t days = (365 * years + intervening_leap_days
158 + (yday - tp->tm_yday));
159 return (60 * (60 * (24 * days + (hour - tp->tm_hour))
160 + (min - tp->tm_min))
161 + (sec - tp->tm_sec));
165 static time_t localtime_offset;
167 /* Convert *TP to a time_t value. */
173 /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
174 time zone names contained in the external variable `tzname' shall
175 be set as if the tzset() function had been called. */
179 return __mktime_internal (tp, localtime_r, &localtime_offset);
182 /* Convert *TP to a time_t value, inverting
183 the monotonic and mostly-unit-linear conversion function CONVERT.
184 Use *OFFSET to keep track of a guess at the offset of the result,
185 compared to what the result would be for UTC without leap seconds.
186 If *OFFSET's guess is correct, only one CONVERT call is needed. */
188 __mktime_internal (tp, convert, offset)
190 struct tm *(*convert) __P ((const time_t *, struct tm *));
196 /* The maximum number of probes (calls to CONVERT) should be enough
197 to handle any combinations of time zone rule changes, solar time,
198 and leap seconds. Posix.1 prohibits leap seconds, but some hosts
200 int remaining_probes = 4;
202 /* Time requested. Copy it in case CONVERT modifies *TP; this can
203 occur if TP is localtime's returned value and CONVERT is localtime. */
204 int sec = tp->tm_sec;
205 int min = tp->tm_min;
206 int hour = tp->tm_hour;
207 int mday = tp->tm_mday;
208 int mon = tp->tm_mon;
209 int year_requested = tp->tm_year;
210 int isdst = tp->tm_isdst;
212 /* Ensure that mon is in range, and set year accordingly. */
213 int mon_remainder = mon % 12;
214 int negative_mon_remainder = mon_remainder < 0;
215 int mon_years = mon / 12 - negative_mon_remainder;
216 int year = year_requested + mon_years;
218 /* The other values need not be in range:
219 the remaining code handles minor overflows correctly,
220 assuming int and time_t arithmetic wraps around.
221 Major overflows are caught at the end. */
223 /* Calculate day of year from year, month, and day of month.
224 The result need not be in range. */
225 int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
226 [mon_remainder + 12 * negative_mon_remainder])
229 #if LEAP_SECONDS_POSSIBLE
230 /* Handle out-of-range seconds specially,
231 since ydhms_tm_diff assumes every minute has 60 seconds. */
232 int sec_requested = sec;
239 /* Invert CONVERT by probing. First assume the same offset as last time.
240 Then repeatedly use the error to improve the guess. */
242 tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
243 tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
244 t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);
246 for (t = t0 + *offset;
247 (dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm)));
249 if (--remaining_probes == 0)
252 /* Check whether tm.tm_isdst has the requested value, if any. */
253 if (0 <= isdst && 0 <= tm.tm_isdst)
255 int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
258 /* Move two hours in the direction indicated by the disagreement,
259 probe some more, and switch to a new time if found.
260 The largest known fallback due to daylight savings is two hours:
261 once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */
262 time_t ot = t - 2 * 60 * 60 * dst_diff;
263 while (--remaining_probes != 0)
266 if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
267 (*convert) (&ot, &otm))))
274 break; /* Avoid a redundant probe. */
281 #if LEAP_SECONDS_POSSIBLE
282 if (sec_requested != tm.tm_sec)
284 /* Adjust time to reflect the tm_sec requested, not the normalized value.
285 Also, repair any damage from a false match due to a leap second. */
286 t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
287 (*convert) (&t, &tm);
291 if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
293 /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
294 so check for major overflows. A gross check suffices,
295 since if t has overflowed, it is off by a multiple of
296 TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of
297 the difference that is bounded by a small value. */
299 double dyear = (double) year_requested + mon_years - tm.tm_year;
300 double dday = 366 * dyear + mday;
301 double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
303 if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec))
312 weak_alias (mktime, timelocal)
322 return ((a->tm_sec ^ b->tm_sec)
323 | (a->tm_min ^ b->tm_min)
324 | (a->tm_hour ^ b->tm_hour)
325 | (a->tm_mday ^ b->tm_mday)
326 | (a->tm_mon ^ b->tm_mon)
327 | (a->tm_year ^ b->tm_year)
328 | (a->tm_mday ^ b->tm_mday)
329 | (a->tm_yday ^ b->tm_yday)
330 | (a->tm_isdst ^ b->tm_isdst));
337 printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
338 tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
339 tp->tm_hour, tp->tm_min, tp->tm_sec,
340 tp->tm_yday, tp->tm_wday, tp->tm_isdst);
344 check_result (tk, tmk, tl, tml)
350 if (tk != tl || not_equal_tm (&tmk, &tml))
354 printf (")\nyields (");
356 printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
369 struct tm tm, tmk, tml;
373 if ((argc == 3 || argc == 4)
374 && (sscanf (argv[1], "%d-%d-%d%c",
375 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
377 && (sscanf (argv[2], "%d:%d:%d%c",
378 &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
381 tm.tm_year -= TM_YEAR_BASE;
383 tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
386 tml = *localtime (&tl);
387 printf ("mktime returns %ld == ", (long) tl);
390 status = check_result (tl, tmk, tl, tml);
392 else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
394 time_t from = atol (argv[1]);
395 time_t by = atol (argv[2]);
396 time_t to = atol (argv[3]);
399 for (tl = from; tl <= to; tl += by)
401 tml = *localtime (&tl);
404 status |= check_result (tk, tmk, tl, tml);
407 for (tl = from; tl <= to; tl += by)
409 /* Null benchmark. */
410 tml = *localtime (&tl);
413 status |= check_result (tk, tmk, tl, tml);
418 \t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
419 \t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
420 \t%s FROM BY TO - # Do not test those values (for benchmark).\n",
421 argv[0], argv[0], argv[0]);
430 compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"