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 #define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \
79 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
82 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
85 #define TM_YEAR_BASE 1900
86 #define EPOCH_YEAR 1970
89 /* Nonzero if YEAR is a leap year (every 4 years,
90 except every 100th isn't, and every 400th is). */
91 #define __isleap(year) \
92 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
95 /* How many days come before each month (0-12). */
96 const unsigned short int __mon_yday[2][13] =
99 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
101 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
104 static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *));
105 time_t __mktime_internal __P ((struct tm *,
106 struct tm *(*) (const time_t *, struct tm *),
111 #define localtime_r __localtime_r
113 #if ! HAVE_LOCALTIME_R && ! defined (localtime_r)
114 /* Approximate localtime_r as best we can in its absence. */
115 #define localtime_r my_localtime_r
116 static struct tm *localtime_r __P ((const time_t *, struct tm *));
122 struct tm *l = localtime (t);
128 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
132 /* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
133 measured in seconds, ignoring leap seconds.
134 YEAR uses the same numbering as TM->tm_year.
135 All values are in range, except possibly YEAR.
136 If overflow occurs, yield the low order bits of the correct answer. */
138 ydhms_tm_diff (year, yday, hour, min, sec, tp)
139 int year, yday, hour, min, sec;
142 /* Compute intervening leap days correctly even if year is negative.
143 Take care to avoid int overflow. time_t overflow is OK, since
144 only the low order bits of the correct time_t answer are needed.
145 Don't convert to time_t until after all divisions are done, since
146 time_t might be unsigned. */
147 int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
148 int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
149 int a100 = a4 / 25 - (a4 % 25 < 0);
150 int b100 = b4 / 25 - (b4 % 25 < 0);
151 int a400 = a100 >> 2;
152 int b400 = b100 >> 2;
153 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
154 time_t years = year - (time_t) tp->tm_year;
155 time_t days = (365 * years + intervening_leap_days
156 + (yday - tp->tm_yday));
157 return (60 * (60 * (24 * days + (hour - tp->tm_hour))
158 + (min - tp->tm_min))
159 + (sec - tp->tm_sec));
163 static time_t localtime_offset;
165 /* Convert *TP to a time_t value. */
171 /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
172 time zone names contained in the external variable `tzname' shall
173 be set as if the tzset() function had been called. */
177 return __mktime_internal (tp, localtime_r, &localtime_offset);
180 /* Convert *TP to a time_t value, inverting
181 the monotonic and mostly-unit-linear conversion function CONVERT.
182 Use *OFFSET to keep track of a guess at the offset of the result,
183 compared to what the result would be for UTC without leap seconds.
184 If *OFFSET's guess is correct, only one CONVERT call is needed. */
186 __mktime_internal (tp, convert, offset)
188 struct tm *(*convert) __P ((const time_t *, struct tm *));
194 /* The maximum number of probes (calls to CONVERT) should be enough
195 to handle any combinations of time zone rule changes, solar time,
196 and leap seconds. Posix.1 prohibits leap seconds, but some hosts
198 int remaining_probes = 4;
200 /* Time requested. Copy it in case CONVERT modifies *TP; this can
201 occur if TP is localtime's returned value and CONVERT is localtime. */
202 int sec = tp->tm_sec;
203 int min = tp->tm_min;
204 int hour = tp->tm_hour;
205 int mday = tp->tm_mday;
206 int mon = tp->tm_mon;
207 int year_requested = tp->tm_year;
208 int isdst = tp->tm_isdst;
210 /* Ensure that mon is in range, and set year accordingly. */
211 int mon_remainder = mon % 12;
212 int negative_mon_remainder = mon_remainder < 0;
213 int mon_years = mon / 12 - negative_mon_remainder;
214 int year = year_requested + mon_years;
216 /* The other values need not be in range:
217 the remaining code handles minor overflows correctly,
218 assuming int and time_t arithmetic wraps around.
219 Major overflows are caught at the end. */
221 /* Calculate day of year from year, month, and day of month.
222 The result need not be in range. */
223 int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
224 [mon_remainder + 12 * negative_mon_remainder])
227 #if LEAP_SECONDS_POSSIBLE
228 /* Handle out-of-range seconds specially,
229 since ydhms_tm_diff assumes every minute has 60 seconds. */
230 int sec_requested = sec;
237 /* Invert CONVERT by probing. First assume the same offset as last time.
238 Then repeatedly use the error to improve the guess. */
240 tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
241 tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
242 t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);
244 for (t = t0 + *offset;
245 (dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm)));
247 if (--remaining_probes == 0)
250 /* Check whether tm.tm_isdst has the requested value, if any. */
251 if (0 <= isdst && 0 <= tm.tm_isdst)
253 int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
256 /* Move two hours in the direction indicated by the disagreement,
257 probe some more, and switch to a new time if found.
258 The largest known fallback due to daylight savings is two hours:
259 once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */
260 time_t ot = t - 2 * 60 * 60 * dst_diff;
261 while (--remaining_probes != 0)
264 if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
265 (*convert) (&ot, &otm))))
272 break; /* Avoid a redundant probe. */
279 #if LEAP_SECONDS_POSSIBLE
280 if (sec_requested != tm.tm_sec)
282 /* Adjust time to reflect the tm_sec requested, not the normalized value.
283 Also, repair any damage from a false match due to a leap second. */
284 t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
285 (*convert) (&t, &tm);
289 if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
291 /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
292 so check for major overflows. A gross check suffices,
293 since if t has overflowed, it is off by a multiple of
294 TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of
295 the difference that is bounded by a small value. */
297 double dyear = (double) year_requested + mon_years - tm.tm_year;
298 double dday = 366 * dyear + mday;
299 double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
301 if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec))
310 weak_alias (mktime, timelocal)
320 return ((a->tm_sec ^ b->tm_sec)
321 | (a->tm_min ^ b->tm_min)
322 | (a->tm_hour ^ b->tm_hour)
323 | (a->tm_mday ^ b->tm_mday)
324 | (a->tm_mon ^ b->tm_mon)
325 | (a->tm_year ^ b->tm_year)
326 | (a->tm_mday ^ b->tm_mday)
327 | (a->tm_yday ^ b->tm_yday)
328 | (a->tm_isdst ^ b->tm_isdst));
335 printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
336 tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
337 tp->tm_hour, tp->tm_min, tp->tm_sec,
338 tp->tm_yday, tp->tm_wday, tp->tm_isdst);
342 check_result (tk, tmk, tl, tml)
348 if (tk != tl || not_equal_tm (&tmk, &tml))
352 printf (")\nyields (");
354 printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
367 struct tm tm, tmk, tml;
371 if ((argc == 3 || argc == 4)
372 && (sscanf (argv[1], "%d-%d-%d%c",
373 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
375 && (sscanf (argv[2], "%d:%d:%d%c",
376 &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
379 tm.tm_year -= TM_YEAR_BASE;
381 tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
384 tml = *localtime (&tl);
385 printf ("mktime returns %ld == ", (long) tl);
388 status = check_result (tl, tmk, tl, tml);
390 else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
392 time_t from = atol (argv[1]);
393 time_t by = atol (argv[2]);
394 time_t to = atol (argv[3]);
397 for (tl = from; tl <= to; tl += by)
399 tml = *localtime (&tl);
402 status |= check_result (tk, tmk, tl, tml);
405 for (tl = from; tl <= to; tl += by)
407 /* Null benchmark. */
408 tml = *localtime (&tl);
411 status |= check_result (tk, tmk, tl, tml);
416 \t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
417 \t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
418 \t%s FROM BY TO - # Do not test those values (for benchmark).\n",
419 argv[0], argv[0], argv[0]);
428 compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"