blob: 7888ee152771e8a33db91f6d3a5f9e86e52fc86d [file] [log] [blame]
Jari Aalto31859422009-01-12 13:36:28 +00001/* mktime - convert struct tm to a time_t value */
2
Jari Aalto7117c2d2002-07-17 14:10:11 +00003/* Copyright (C) 1993-2002 Free Software Foundation, Inc.
Jari Aalto31859422009-01-12 13:36:28 +00004
5 This file is part of GNU Bash, the Bourne Again SHell.
Jari Aalto7117c2d2002-07-17 14:10:11 +00006 Contributed by Paul Eggert (eggert@twinsun.com).
7
Jari Aalto31859422009-01-12 13:36:28 +00008 Bash is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
Jari Aalto7117c2d2002-07-17 14:10:11 +000012
Jari Aalto31859422009-01-12 13:36:28 +000013 Bash is distributed in the hope that it will be useful,
Jari Aalto7117c2d2002-07-17 14:10:11 +000014 but WITHOUT ANY WARRANTY; without even the implied warranty of
Jari Aalto31859422009-01-12 13:36:28 +000015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
Jari Aalto7117c2d2002-07-17 14:10:11 +000017
Jari Aalto31859422009-01-12 13:36:28 +000018 You should have received a copy of the GNU General Public License
19 along with Bash. If not, see <http://www.gnu.org/licenses/>.
20*/
Jari Aalto7117c2d2002-07-17 14:10:11 +000021/* Define this to have a standalone program to test this implementation of
22 mktime. */
23/* #define DEBUG 1 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#ifdef _LIBC
30# define HAVE_LIMITS_H 1
31# define HAVE_LOCALTIME_R 1
32# define STDC_HEADERS 1
33#endif
34
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
40#endif
41
42#ifndef VMS
43#include <sys/types.h> /* Some systems define `time_t' here. */
Jari Aalto7117c2d2002-07-17 14:10:11 +000044#endif
45#include <time.h>
46
47#if HAVE_LIMITS_H
48#include <limits.h>
49#endif
50
Jari Aalto31859422009-01-12 13:36:28 +000051#include "bashansi.h"
52
Jari Aalto7117c2d2002-07-17 14:10:11 +000053#if DEBUG
54#include <stdio.h>
Jari Aalto7117c2d2002-07-17 14:10:11 +000055/* Make it work even if the system's libc has its own mktime routine. */
56#define mktime my_mktime
57#endif /* DEBUG */
58
59#ifndef __P
60#if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
61#define __P(args) args
62#else
63#define __P(args) ()
64#endif /* GCC. */
65#endif /* Not __P. */
66
67#ifndef CHAR_BIT
68#define CHAR_BIT 8
69#endif
70
71#ifndef INT_MIN
72#define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
73#endif
74#ifndef INT_MAX
75#define INT_MAX (~0 - INT_MIN)
76#endif
77
78#ifndef TIME_T_MIN
79#define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \
80 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
81#endif
82#ifndef TIME_T_MAX
83#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
84#endif
85
86#define TM_YEAR_BASE 1900
87#define EPOCH_YEAR 1970
88
89#ifndef __isleap
90/* Nonzero if YEAR is a leap year (every 4 years,
91 except every 100th isn't, and every 400th is). */
92#define __isleap(year) \
93 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
94#endif
95
96/* How many days come before each month (0-12). */
97const unsigned short int __mon_yday[2][13] =
98 {
99 /* Normal years. */
100 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
101 /* Leap years. */
102 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
103 };
104
105static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *));
106time_t __mktime_internal __P ((struct tm *,
107 struct tm *(*) (const time_t *, struct tm *),
108 time_t *));
109
110
111static struct tm *my_localtime_r __P ((const time_t *, struct tm *));
112static struct tm *
113my_localtime_r (t, tp)
114 const time_t *t;
115 struct tm *tp;
116{
117 struct tm *l = localtime (t);
118 if (! l)
119 return 0;
120 *tp = *l;
121 return tp;
122}
123
124
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. */
130static time_t
131ydhms_tm_diff (year, yday, hour, min, sec, tp)
132 int year, yday, hour, min, sec;
133 const struct tm *tp;
134{
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));
153}
154
155
156static time_t localtime_offset;
157
158/* Convert *TP to a time_t value. */
159time_t
160mktime (tp)
161 struct tm *tp;
162{
163#ifdef _LIBC
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. */
167 __tzset ();
168#endif
169
170 return __mktime_internal (tp, my_localtime_r, &localtime_offset);
171}
172
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. */
178time_t
179__mktime_internal (tp, convert, offset)
180 struct tm *tp;
181 struct tm *(*convert) __P ((const time_t *, struct tm *));
182 time_t *offset;
183{
184 time_t t, dt, t0;
185 struct tm tm;
186
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
190 have them anyway. */
191 int remaining_probes = 4;
192
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;
202
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;
208
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. */
213
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])
218 + mday - 1);
219
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;
224 if (sec < 0)
225 sec = 0;
226 if (59 < sec)
227 sec = 59;
228#endif
229
230 /* Invert CONVERT by probing. First assume the same offset as last time.
231 Then repeatedly use the error to improve the guess. */
232
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);
236
237 for (t = t0 + *offset;
238 (dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm)));
239 t += dt)
240 if (--remaining_probes == 0)
241 return -1;
242
243 /* Check whether tm.tm_isdst has the requested value, if any. */
244 if (0 <= isdst && 0 <= tm.tm_isdst)
245 {
246 int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
247 if (dst_diff)
248 {
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)
255 {
256 struct tm otm;
257 if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
258 (*convert) (&ot, &otm))))
259 {
260 t = ot;
261 tm = otm;
262 break;
263 }
264 if ((ot += dt) == t)
265 break; /* Avoid a redundant probe. */
266 }
267 }
268 }
269
270 *offset = t - t0;
271
272#if LEAP_SECONDS_POSSIBLE
273 if (sec_requested != tm.tm_sec)
274 {
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);
279 }
280#endif
281
282 if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
283 {
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. */
289
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;
293
294 if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec))
295 return -1;
296 }
297
298 *tp = tm;
299 return t;
300}
301
302#ifdef weak_alias
303weak_alias (mktime, timelocal)
304#endif
305
306#if DEBUG
307
308static int
309not_equal_tm (a, b)
310 struct tm *a;
311 struct tm *b;
312{
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));
322}
323
324static void
325print_tm (tp)
326 struct tm *tp;
327{
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);
332}
333
334static int
335check_result (tk, tmk, tl, tml)
336 time_t tk;
337 struct tm tmk;
338 time_t tl;
339 struct tm tml;
340{
341 if (tk != tl || not_equal_tm (&tmk, &tml))
342 {
343 printf ("mktime (");
344 print_tm (&tmk);
345 printf (")\nyields (");
346 print_tm (&tml);
347 printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
348 return 1;
349 }
350
351 return 0;
352}
353
354int
355main (argc, argv)
356 int argc;
357 char **argv;
358{
359 int status = 0;
360 struct tm tm, tmk, tml;
361 time_t tk, tl;
362 char trailer;
363
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)
367 == 3)
368 && (sscanf (argv[2], "%d:%d:%d%c",
369 &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
370 == 3))
371 {
372 tm.tm_year -= TM_YEAR_BASE;
373 tm.tm_mon--;
374 tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
375 tmk = tm;
376 tl = mktime (&tmk);
377 tml = *localtime (&tl);
378 printf ("mktime returns %ld == ", (long) tl);
379 print_tm (&tmk);
380 printf ("\n");
381 status = check_result (tl, tmk, tl, tml);
382 }
383 else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
384 {
385 time_t from = atol (argv[1]);
386 time_t by = atol (argv[2]);
387 time_t to = atol (argv[3]);
388
389 if (argc == 4)
390 for (tl = from; tl <= to; tl += by)
391 {
392 tml = *localtime (&tl);
393 tmk = tml;
394 tk = mktime (&tmk);
395 status |= check_result (tk, tmk, tl, tml);
396 }
397 else
398 for (tl = from; tl <= to; tl += by)
399 {
400 /* Null benchmark. */
401 tml = *localtime (&tl);
402 tmk = tml;
403 tk = tl;
404 status |= check_result (tk, tmk, tl, tml);
405 }
406 }
407 else
408 printf ("Usage:\
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]);
413
414 return status;
415}
416
417#endif /* DEBUG */
418
419/*
420Local Variables:
421compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"
422End:
423*/