]> git.ipfire.org Git - thirdparty/glibc.git/blob - time/mktime.c
Fri Sep 29 03:43:51 1995 Paul Eggert <eggert@twinsun.com>
[thirdparty/glibc.git] / time / mktime.c
1 /* Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
2 Contributed by Paul Eggert (eggert@twinsun.com).
3
4 This file is part of the GNU C Library.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If
18 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
19 Cambridge, MA 02139, USA. */
20
21 /* 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 /* 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
34 #endif
35
36 #include <sys/types.h> /* Some systems define `time_t' here. */
37 #include <time.h>
38
39 #if __STDC__ || __GNU_LIBRARY__ || STDC_HEADERS
40 #include <limits.h>
41 #endif
42
43 #if DEBUG
44 #include <stdio.h>
45 #if __STDC__ || __GNU_LIBRARY__ || STDC_HEADERS
46 #include <stdlib.h>
47 #endif
48 /* Make it work even if the system's libc has its own mktime routine. */
49 #define mktime my_mktime
50 #endif /* DEBUG */
51
52 #ifndef __P
53 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
54 #define __P(args) args
55 #else
56 #define __P(args) ()
57 #endif /* GCC. */
58 #endif /* Not __P. */
59
60 #ifndef CHAR_BIT
61 #define CHAR_BIT 8
62 #endif
63
64 #ifndef INT_MIN
65 #define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
66 #endif
67 #ifndef INT_MAX
68 #define INT_MAX (~0 - INT_MIN)
69 #endif
70
71 #ifndef TIME_T_MIN
72 #define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \
73 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
74 #endif
75 #ifndef TIME_T_MAX
76 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
77 #endif
78
79 #define TM_YEAR_BASE 1900
80 #define EPOCH_YEAR 1970
81
82 #ifndef __isleap
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))
87 #endif
88
89 /* How many days come before each month (0-12). */
90 const unsigned short int __mon_yday[2][13] =
91 {
92 /* Normal years. */
93 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
94 /* Leap years. */
95 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
96 };
97
98 static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *));
99 time_t __mktime_internal __P ((struct tm *,
100 struct tm *(*) (const time_t *, struct tm *),
101 time_t *));
102
103
104 #if ! HAVE_LOCALTIME_R && ! defined (localtime_r)
105 #ifdef _LIBC
106 #define localtime_r __localtime_r
107 #else
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 *));
111 static struct tm *
112 localtime_r (t, tp)
113 const time_t *t;
114 struct tm *tp;
115 {
116 struct tm *l = localtime (t);
117 if (! l)
118 return 0;
119 *tp = *l;
120 return tp;
121 }
122 #endif /* ! _LIBC */
123 #endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
124
125
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. */
131 static time_t
132 ydhms_tm_diff (year, yday, hour, min, sec, tp)
133 int year, yday, hour, min, sec;
134 const struct tm *tp;
135 {
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));
146 }
147
148
149 /* Convert *TP to a time_t value. */
150 time_t
151 mktime (tp)
152 struct tm *tp;
153 {
154 static time_t localtime_offset;
155 return __mktime_internal (tp, localtime_r, &localtime_offset);
156 }
157
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. */
163 time_t
164 __mktime_internal (tp, convert, offset)
165 struct tm *tp;
166 struct tm *(*convert) __P ((const time_t *, struct tm *));
167 time_t *offset;
168 {
169 time_t t, dt, t0;
170 struct tm tm;
171
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
175 have them anyway. */
176 int remaining_probes = 4;
177
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;
187
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;
193
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. */
198
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])
203 + mday - 1);
204
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;
209 if (sec < 0)
210 sec = 0;
211 if (59 < sec)
212 sec = 59;
213 #endif
214
215 /* Invert CONVERT by probing. First assume the same offset as last time.
216 Then repeatedly use the error to improve the guess. */
217
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);
221
222 for (t = t0 + *offset;
223 (dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm)));
224 t += dt)
225 if (--remaining_probes == 0)
226 return -1;
227
228 /* Check whether tm.tm_isdst has the requested value, if any. */
229 if (0 <= isdst && 0 <= tm.tm_isdst)
230 {
231 int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
232 if (dst_diff)
233 {
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)
240 {
241 struct tm otm;
242 if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
243 (*convert) (&ot, &otm))))
244 {
245 t = ot;
246 tm = otm;
247 break;
248 }
249 if ((ot += dt) == t)
250 break; /* Avoid a redundant probe. */
251 }
252 }
253 }
254
255 *offset = t - t0;
256
257 #if LEAP_SECONDS_POSSIBLE
258 if (sec_requested != tm.tm_sec)
259 {
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);
264 }
265 #endif
266
267 if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
268 {
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. */
274
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;
278
279 if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec))
280 return -1;
281 }
282
283 *tp = tm;
284 return t;
285 }
286
287 #ifdef weak_alias
288 weak_alias (mktime, timelocal)
289 #endif
290 \f
291 #if DEBUG
292
293 static int
294 not_equal_tm (a, b)
295 struct tm *a;
296 struct tm *b;
297 {
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));
307 }
308
309 static void
310 print_tm (tp)
311 struct tm *tp;
312 {
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);
317 }
318
319 static int
320 check_result (tk, tmk, tl, tml)
321 time_t tk;
322 struct tm tmk;
323 time_t tl;
324 struct tm tml;
325 {
326 if (tk != tl || not_equal_tm (&tmk, &tml))
327 {
328 printf ("mktime (");
329 print_tm (&tmk);
330 printf (")\nyields (");
331 print_tm (&tml);
332 printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
333 return 1;
334 }
335
336 return 0;
337 }
338
339 int
340 main (argc, argv)
341 int argc;
342 char **argv;
343 {
344 int status = 0;
345 struct tm tm, tmk, tml;
346 time_t tk, tl;
347 char trailer;
348
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)
352 == 3)
353 && (sscanf (argv[2], "%d:%d:%d%c",
354 &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
355 == 3))
356 {
357 tm.tm_year -= TM_YEAR_BASE;
358 tm.tm_mon--;
359 tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
360 tmk = tm;
361 tl = mktime (&tmk);
362 tml = *localtime (&tl);
363 printf ("mktime returns %ld == ", (long) tl);
364 print_tm (&tmk);
365 printf ("\n");
366 status = check_result (tl, tmk, tl, tml);
367 }
368 else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
369 {
370 time_t from = atol (argv[1]);
371 time_t by = atol (argv[2]);
372 time_t to = atol (argv[3]);
373
374 if (argc == 4)
375 for (tl = from; tl <= to; tl += by)
376 {
377 tml = *localtime (&tl);
378 tmk = tml;
379 tk = mktime (&tmk);
380 status |= check_result (tk, tmk, tl, tml);
381 }
382 else
383 for (tl = from; tl <= to; tl += by)
384 {
385 /* Null benchmark. */
386 tml = *localtime (&tl);
387 tmk = tml;
388 tk = tl;
389 status |= check_result (tk, tmk, tl, tml);
390 }
391 }
392 else
393 printf ("Usage:\
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]);
398
399 return status;
400 }
401
402 #endif /* DEBUG */
403 \f
404 /*
405 Local Variables:
406 compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"
407 End:
408 */