]> git.ipfire.org Git - thirdparty/glibc.git/blame - time/mktime.c
mktime: fix non-EOVERFLOW errno handling
[thirdparty/glibc.git] / time / mktime.c
CommitLineData
6226efbd 1/* Convert a 'struct tm' to a time_t value.
688903eb 2 Copyright (C) 1993-2018 Free Software Foundation, Inc.
5290baf0 3 This file is part of the GNU C Library.
41aba3d7 4 Contributed by Paul Eggert <eggert@twinsun.com>.
28f540f4 5
5290baf0 6 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
28f540f4 10
5290baf0
UD
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
41bdb6e2 14 Lesser General Public License for more details.
28f540f4 15
41bdb6e2 16 You should have received a copy of the GNU Lesser General Public
59ba27a6 17 License along with the GNU C Library; if not, see
8e6fd2bd 18 <https://www.gnu.org/licenses/>. */
28f540f4 19
8e6fd2bd 20/* Define this to 1 to have a standalone program to test this implementation of
28f540f4 21 mktime. */
8e6fd2bd
PE
22#ifndef DEBUG_MKTIME
23# define DEBUG_MKTIME 0
24#endif
28f540f4 25
8e6fd2bd
PE
26/* The following macros influence what gets defined when this file is compiled:
27
28 Macro/expression Which gnulib module This compilation unit
29 should define
30
31 _LIBC (glibc proper) mktime
32
33 NEED_MKTIME_WORKING mktime rpl_mktime
34 || NEED_MKTIME_WINDOWS
35
36 NEED_MKTIME_INTERNAL mktime-internal mktime_internal
37
38 DEBUG_MKTIME (defined manually) my_mktime, main
39 */
40
41#if !defined _LIBC && !DEBUG_MKTIME
de20b81a 42# include <libc-config.h>
28f540f4
RM
43#endif
44
80fd7387 45/* Assume that leap seconds are possible, unless told otherwise.
6226efbd 46 If the host has a 'zic' command with a '-L leapsecondfilename' option,
80fd7387
RM
47 then it supports leap seconds; otherwise it probably doesn't. */
48#ifndef LEAP_SECONDS_POSSIBLE
9c2322bc 49# define LEAP_SECONDS_POSSIBLE 1
80fd7387
RM
50#endif
51
28f540f4
RM
52#include <time.h>
53
de20b81a 54#include <errno.h>
85e07670 55#include <limits.h>
8e6fd2bd
PE
56#include <stdbool.h>
57#include <stdlib.h>
58#include <string.h>
28f540f4 59
8e6fd2bd
PE
60#include <intprops.h>
61#include <verify.h>
9b5204dd 62
8e6fd2bd 63#if DEBUG_MKTIME
9c2322bc 64# include <stdio.h>
80fd7387 65/* Make it work even if the system's libc has its own mktime routine. */
826dd0ab 66# undef mktime
9c2322bc 67# define mktime my_mktime
f2b3078e 68#endif /* DEBUG_MKTIME */
28f540f4 69
8e6fd2bd
PE
70#ifndef NEED_MKTIME_INTERNAL
71# define NEED_MKTIME_INTERNAL 0
72#endif
73#ifndef NEED_MKTIME_WINDOWS
74# define NEED_MKTIME_WINDOWS 0
75#endif
76#ifndef NEED_MKTIME_WORKING
77# define NEED_MKTIME_WORKING DEBUG_MKTIME
78#endif
79
80#include "mktime-internal.h"
81
5a580643 82#if !defined _LIBC && (NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS)
8e6fd2bd
PE
83static void
84my_tzset (void)
85{
86# if NEED_MKTIME_WINDOWS
87 /* Rectify the value of the environment variable TZ.
88 There are four possible kinds of such values:
89 - Traditional US time zone names, e.g. "PST8PDT". Syntax: see
90 <https://msdn.microsoft.com/en-us/library/90s5c885.aspx>
91 - Time zone names based on geography, that contain one or more
92 slashes, e.g. "Europe/Moscow".
93 - Time zone names based on geography, without slashes, e.g.
94 "Singapore".
95 - Time zone names that contain explicit DST rules. Syntax: see
96 <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03>
97 The Microsoft CRT understands only the first kind. It produces incorrect
98 results if the value of TZ is of the other kinds.
99 But in a Cygwin environment, /etc/profile.d/tzset.sh sets TZ to a value
100 of the second kind for most geographies, or of the first kind in a few
101 other geographies. If it is of the second kind, neutralize it. For the
102 Microsoft CRT, an absent or empty TZ means the time zone that the user
103 has set in the Windows Control Panel.
104 If the value of TZ is of the third or fourth kind -- Cygwin programs
105 understand these syntaxes as well --, it does not matter whether we
106 neutralize it or not, since these values occur only when a Cygwin user
107 has set TZ explicitly; this case is 1. rare and 2. under the user's
108 responsibility. */
109 const char *tz = getenv ("TZ");
110 if (tz != NULL && strchr (tz, '/') != NULL)
111 _putenv ("TZ=");
112# elif HAVE_TZSET
113 tzset ();
62bdf9a6 114# endif
8e6fd2bd
PE
115}
116# undef __tzset
117# define __tzset() my_tzset ()
62bdf9a6
PE
118#endif
119
8e6fd2bd
PE
120#if defined _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_INTERNAL
121
122/* A signed type that can represent an integer number of years
efbdddc3 123 multiplied by four times the number of seconds in a year. It is
8e6fd2bd 124 needed when converting a tm_year value times the number of seconds
efbdddc3 125 in a year. The factor of four comes because these products need
8e6fd2bd 126 to be subtracted from each other, and sometimes with an offset
efbdddc3
PE
127 added to them, and then with another timestamp added, without
128 worrying about overflow.
8e6fd2bd
PE
129
130 Much of the code uses long_int to represent time_t values, to
131 lessen the hassle of dealing with platforms where time_t is
132 unsigned, and because long_int should suffice to represent all
133 time_t values that mktime can generate even on platforms where
134 time_t is excessively wide. */
f04dfbc2 135
efbdddc3 136#if INT_MAX <= LONG_MAX / 4 / 366 / 24 / 60 / 60
f04dfbc2
PE
137typedef long int long_int;
138#else
139typedef long long int long_int;
140#endif
efbdddc3 141verify (INT_MAX <= TYPE_MAXIMUM (long_int) / 4 / 366 / 24 / 60 / 60);
f04dfbc2 142
1c67fabd 143/* Shift A right by B bits portably, by dividing A by 2**B and
8e6fd2bd
PE
144 truncating towards minus infinity. B should be in the range 0 <= B
145 <= LONG_INT_BITS - 2, where LONG_INT_BITS is the number of useful
146 bits in a long_int. LONG_INT_BITS is at least 32.
1c67fabd
RM
147
148 ISO C99 says that A >> B is implementation-defined if A < 0. Some
149 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
150 right in the usual way when A < 0, so SHR falls back on division if
151 ordinary A >> B doesn't seem to be the usual signed shift. */
28f540f4 152
8e6fd2bd
PE
153static long_int
154shr (long_int a, int b)
155{
156 long_int one = 1;
157 return (-one >> 1 == -1
158 ? a >> b
159 : a / (one << b) - (a % (one << b) < 0));
160}
161
162/* Bounds for the intersection of time_t and long_int. */
163
164static long_int const mktime_min
165 = ((TYPE_SIGNED (time_t) && TYPE_MINIMUM (time_t) < TYPE_MINIMUM (long_int))
166 ? TYPE_MINIMUM (long_int) : TYPE_MINIMUM (time_t));
167static long_int const mktime_max
168 = (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (time_t)
169 ? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (time_t));
170
171verify (TYPE_IS_INTEGER (time_t));
a28a0500 172
80fd7387 173#define EPOCH_YEAR 1970
a28a0500 174#define TM_YEAR_BASE 1900
8e6fd2bd 175verify (TM_YEAR_BASE % 100 == 0);
28f540f4 176
8e6fd2bd
PE
177/* Is YEAR + TM_YEAR_BASE a leap year? */
178static bool
f04dfbc2 179leapyear (long_int year)
72035294
RM
180{
181 /* Don't add YEAR to TM_YEAR_BASE, as that might overflow.
182 Also, work even if YEAR is negative. */
183 return
184 ((year & 3) == 0
185 && (year % 100 != 0
186 || ((year / 100) & 3) == (- (TM_YEAR_BASE / 100) & 3)));
187}
28f540f4 188
80fd7387 189/* How many days come before each month (0-12). */
8592ae92
UD
190#ifndef _LIBC
191static
192#endif
80fd7387
RM
193const unsigned short int __mon_yday[2][13] =
194 {
195 /* Normal years. */
196 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
197 /* Leap years. */
198 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
199 };
28f540f4 200
28f540f4 201
8e6fd2bd
PE
202/* Do the values A and B differ according to the rules for tm_isdst?
203 A and B differ if one is zero and the other positive. */
204static bool
ce73d683
PE
205isdst_differ (int a, int b)
206{
207 return (!a != !b) && (0 <= a) && (0 <= b);
208}
209
e507cc56
RM
210/* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) -
211 (YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks
8e6fd2bd 212 were not adjusted between the timestamps.
80fd7387 213
e507cc56 214 The YEAR values uses the same numbering as TP->tm_year. Values
efbdddc3
PE
215 need not be in the usual range. However, YEAR1 - YEAR0 must not
216 overflow even when multiplied by three times the number of seconds
217 in a year, and likewise for YDAY1 - YDAY0 and three times the
218 number of seconds in a day. */
e507cc56 219
8e6fd2bd 220static long_int
f04dfbc2 221ydhms_diff (long_int year1, long_int yday1, int hour1, int min1, int sec1,
e507cc56
RM
222 int year0, int yday0, int hour0, int min0, int sec0)
223{
8e6fd2bd 224 verify (-1 / 2 == 0);
e507cc56
RM
225
226 /* Compute intervening leap days correctly even if year is negative.
227 Take care to avoid integer overflow here. */
8e6fd2bd
PE
228 int a4 = shr (year1, 2) + shr (TM_YEAR_BASE, 2) - ! (year1 & 3);
229 int b4 = shr (year0, 2) + shr (TM_YEAR_BASE, 2) - ! (year0 & 3);
e507cc56
RM
230 int a100 = a4 / 25 - (a4 % 25 < 0);
231 int b100 = b4 / 25 - (b4 % 25 < 0);
8e6fd2bd
PE
232 int a400 = shr (a100, 2);
233 int b400 = shr (b100, 2);
e507cc56
RM
234 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
235
8e6fd2bd
PE
236 /* Compute the desired time without overflowing. */
237 long_int years = year1 - year0;
238 long_int days = 365 * years + yday1 - yday0 + intervening_leap_days;
239 long_int hours = 24 * days + hour1 - hour0;
240 long_int minutes = 60 * hours + min1 - min0;
241 long_int seconds = 60 * minutes + sec1 - sec0;
e507cc56
RM
242 return seconds;
243}
244
8e6fd2bd
PE
245/* Return the average of A and B, even if A + B would overflow.
246 Round toward positive infinity. */
247static long_int
248long_int_avg (long_int a, long_int b)
62bdf9a6 249{
8e6fd2bd 250 return shr (a, 1) + shr (b, 1) + ((a | b) & 1);
62bdf9a6 251}
e507cc56 252
86aece3b
PE
253/* Return a long_int value corresponding to (YEAR-YDAY HOUR:MIN:SEC)
254 minus *TP seconds, assuming no clock adjustments occurred between
255 the two timestamps.
256
8e6fd2bd
PE
257 YEAR and YDAY must not be so large that multiplying them by three times the
258 number of seconds in a year (or day, respectively) would overflow long_int.
86aece3b 259 *TP should be in the usual range. */
8e6fd2bd 260static long_int
86aece3b
PE
261tm_diff (long_int year, long_int yday, int hour, int min, int sec,
262 struct tm const *tp)
80fd7387 263{
86aece3b
PE
264 return ydhms_diff (year, yday, hour, min, sec,
265 tp->tm_year, tp->tm_yday,
266 tp->tm_hour, tp->tm_min, tp->tm_sec);
8e6fd2bd
PE
267}
268
269/* Use CONVERT to convert T to a struct tm value in *TM. T must be in
86aece3b
PE
270 range for time_t. Return TM if successful, NULL (setting errno) on
271 failure. */
8e6fd2bd
PE
272static struct tm *
273convert_time (struct tm *(*convert) (const time_t *, struct tm *),
274 long_int t, struct tm *tm)
275{
276 time_t x = t;
277 return convert (&x, tm);
80fd7387
RM
278}
279
fe0ec73e
UD
280/* Use CONVERT to convert *T to a broken down time in *TP.
281 If *T is out of range for conversion, adjust it so that
8e6fd2bd 282 it is the nearest in-range value and then convert that.
86aece3b
PE
283 A value is in range if it fits in both time_t and long_int.
284 Return TP on success, NULL (setting errno) on failure. */
fe0ec73e 285static struct tm *
eda78eec 286ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
8e6fd2bd 287 long_int *t, struct tm *tp)
fe0ec73e 288{
86aece3b
PE
289 long_int t1 = (*t < mktime_min ? mktime_min
290 : *t <= mktime_max ? *t : mktime_max);
291 struct tm *r = convert_time (convert, t1, tp);
292 if (r)
fe0ec73e 293 {
86aece3b
PE
294 *t = t1;
295 return r;
296 }
297 if (errno != EOVERFLOW)
298 return NULL;
fe0ec73e 299
86aece3b
PE
300 long_int bad = t1;
301 long_int ok = 0;
302 struct tm oktm; oktm.tm_sec = -1;
f6b3331b 303
86aece3b
PE
304 /* BAD is a known out-of-range value, and OK is a known in-range one.
305 Use binary search to narrow the range between BAD and OK until
306 they differ by 1. */
307 while (true)
308 {
309 long_int mid = long_int_avg (ok, bad);
310 if (mid == ok || mid == bad)
311 break;
312 if (convert_time (convert, mid, tp))
313 ok = mid, oktm = *tp;
314 else if (errno != EOVERFLOW)
315 return NULL;
316 else
317 bad = mid;
fe0ec73e
UD
318 }
319
86aece3b
PE
320 if (oktm.tm_sec < 0)
321 return NULL;
322 *t = ok;
323 *tp = oktm;
324 return tp;
fe0ec73e
UD
325}
326
327
80fd7387
RM
328/* Convert *TP to a time_t value, inverting
329 the monotonic and mostly-unit-linear conversion function CONVERT.
330 Use *OFFSET to keep track of a guess at the offset of the result,
331 compared to what the result would be for UTC without leap seconds.
e507cc56 332 If *OFFSET's guess is correct, only one CONVERT call is needed.
de20b81a
PE
333 If successful, set *TP to the canonicalized struct tm;
334 otherwise leave *TP alone, return ((time_t) -1) and set errno.
e507cc56 335 This function is external because it is used also by timegm.c. */
80fd7387 336time_t
eda78eec
UD
337__mktime_internal (struct tm *tp,
338 struct tm *(*convert) (const time_t *, struct tm *),
8e6fd2bd 339 mktime_offset_t *offset)
28f540f4 340{
80fd7387
RM
341 struct tm tm;
342
343 /* The maximum number of probes (calls to CONVERT) should be enough
344 to handle any combinations of time zone rule changes, solar time,
25b3b17b
UD
345 leap seconds, and oscillations around a spring-forward gap.
346 POSIX.1 prohibits leap seconds, but some hosts have them anyway. */
347 int remaining_probes = 6;
80fd7387
RM
348
349 /* Time requested. Copy it in case CONVERT modifies *TP; this can
350 occur if TP is localtime's returned value and CONVERT is localtime. */
351 int sec = tp->tm_sec;
352 int min = tp->tm_min;
353 int hour = tp->tm_hour;
354 int mday = tp->tm_mday;
355 int mon = tp->tm_mon;
356 int year_requested = tp->tm_year;
ce73d683 357 int isdst = tp->tm_isdst;
80fd7387 358
b5ef404e 359 /* 1 if the previous probe was DST. */
86aece3b 360 int dst2 = 0;
b5ef404e 361
80fd7387
RM
362 /* Ensure that mon is in range, and set year accordingly. */
363 int mon_remainder = mon % 12;
364 int negative_mon_remainder = mon_remainder < 0;
365 int mon_years = mon / 12 - negative_mon_remainder;
f04dfbc2
PE
366 long_int lyear_requested = year_requested;
367 long_int year = lyear_requested + mon_years;
80fd7387 368
8592ae92 369 /* The other values need not be in range:
8e6fd2bd 370 the remaining code handles overflows correctly. */
80fd7387
RM
371
372 /* Calculate day of year from year, month, and day of month.
373 The result need not be in range. */
e507cc56
RM
374 int mon_yday = ((__mon_yday[leapyear (year)]
375 [mon_remainder + 12 * negative_mon_remainder])
376 - 1);
f04dfbc2
PE
377 long_int lmday = mday;
378 long_int yday = mon_yday + lmday;
e507cc56 379
8e6fd2bd
PE
380 mktime_offset_t off = *offset;
381 int negative_offset_guess;
80fd7387 382
9a0a462c 383 int sec_requested = sec;
55544141 384
e507cc56
RM
385 if (LEAP_SECONDS_POSSIBLE)
386 {
387 /* Handle out-of-range seconds specially,
efbdddc3 388 since ydhms_diff assumes every minute has 60 seconds. */
e507cc56
RM
389 if (sec < 0)
390 sec = 0;
391 if (59 < sec)
392 sec = 59;
393 }
394
395 /* Invert CONVERT by probing. First assume the same offset as last
396 time. */
397
8e6fd2bd 398 INT_SUBTRACT_WRAPV (0, off, &negative_offset_guess);
86aece3b
PE
399 long_int t0 = ydhms_diff (year, yday, hour, min, sec,
400 EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0,
401 negative_offset_guess);
402 long_int t = t0, t1 = t0, t2 = t0;
80fd7387 403
e507cc56 404 /* Repeatedly use the error to improve the guess. */
28f540f4 405
86aece3b
PE
406 while (true)
407 {
408 if (! ranged_convert (convert, &t, &tm))
de20b81a 409 return -1;
86aece3b
PE
410 long_int dt = tm_diff (year, yday, hour, min, sec, &tm);
411 if (dt == 0)
412 break;
413
414 if (t == t1 && t != t2
415 && (tm.tm_isdst < 0
416 || (isdst < 0
417 ? dst2 <= (tm.tm_isdst != 0)
418 : (isdst != 0) != (tm.tm_isdst != 0))))
419 /* We can't possibly find a match, as we are oscillating
420 between two values. The requested time probably falls
421 within a spring-forward gap of size DT. Follow the common
422 practice in this case, which is to return a time that is DT
423 away from the requested time, preferring a time whose
424 tm_isdst differs from the requested value. (If no tm_isdst
425 was requested and only one of the two values has a nonzero
426 tm_isdst, prefer that value.) In practice, this is more
427 useful than returning -1. */
428 goto offset_found;
429
430 remaining_probes--;
431 if (remaining_probes == 0)
432 {
433 __set_errno (EOVERFLOW);
434 return -1;
435 }
436
437 t1 = t2, t2 = t, t += dt, dst2 = tm.tm_isdst != 0;
438 }
80fd7387 439
e507cc56 440 /* We have a match. Check whether tm.tm_isdst has the requested
25b3b17b 441 value, if any. */
ce73d683 442 if (isdst_differ (isdst, tm.tm_isdst))
80fd7387 443 {
c0016081
UD
444 /* tm.tm_isdst has the wrong value. Look for a neighboring
445 time with the right value, and use its UTC offset.
c0016081 446
e507cc56
RM
447 Heuristic: probe the adjacent timestamps in both directions,
448 looking for the desired isdst. This should work for all real
449 time zone histories in the tz database. */
450
451 /* Distance between probes when looking for a DST boundary. In
452 tzdata2003a, the shortest period of DST is 601200 seconds
453 (e.g., America/Recife starting 2000-10-08 01:00), and the
454 shortest period of non-DST surrounded by DST is 694800
455 seconds (Africa/Tunis starting 1943-04-17 01:00). Use the
456 minimum of these two values, so we don't miss these short
457 periods when probing. */
458 int stride = 601200;
459
460 /* The longest period of DST in tzdata2003a is 536454000 seconds
461 (e.g., America/Jujuy starting 1946-10-01 01:00). The longest
462 period of non-DST is much longer, but it makes no real sense
463 to search for more than a year of non-DST, so use the DST
464 max. */
465 int duration_max = 536454000;
466
467 /* Search in both directions, so the maximum distance is half
468 the duration; add the stride to avoid off-by-1 problems. */
469 int delta_bound = duration_max / 2 + stride;
470
471 int delta, direction;
472
473 for (delta = stride; delta < delta_bound; delta += stride)
474 for (direction = -1; direction <= 1; direction += 2)
8e6fd2bd
PE
475 {
476 long_int ot;
477 if (! INT_ADD_WRAPV (t, delta * direction, &ot))
478 {
479 struct tm otm;
86aece3b
PE
480 if (! ranged_convert (convert, &ot, &otm))
481 return -1;
8e6fd2bd
PE
482 if (! isdst_differ (isdst, otm.tm_isdst))
483 {
484 /* We found the desired tm_isdst.
485 Extrapolate back to the desired time. */
86aece3b
PE
486 long_int gt = ot + tm_diff (year, yday, hour, min, sec,
487 &otm);
488 if (mktime_min <= gt && gt <= mktime_max)
489 {
490 if (convert_time (convert, gt, &tm))
491 {
492 t = gt;
493 goto offset_found;
494 }
495 if (errno != EOVERFLOW)
496 return -1;
497 }
8e6fd2bd
PE
498 }
499 }
500 }
86aece3b
PE
501
502 __set_errno (EOVERFLOW);
503 return -1;
80fd7387 504 }
28f540f4 505
e507cc56 506 offset_found:
8e6fd2bd
PE
507 /* Set *OFFSET to the low-order bits of T - T0 - NEGATIVE_OFFSET_GUESS.
508 This is just a heuristic to speed up the next mktime call, and
509 correctness is unaffected if integer overflow occurs here. */
6c90d759
PE
510 INT_SUBTRACT_WRAPV (t, t0, offset);
511 INT_SUBTRACT_WRAPV (*offset, negative_offset_guess, offset);
80fd7387 512
e507cc56 513 if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec)
80fd7387
RM
514 {
515 /* Adjust time to reflect the tm_sec requested, not the normalized value.
516 Also, repair any damage from a false match due to a leap second. */
8e6fd2bd
PE
517 long_int sec_adjustment = sec == 0 && tm.tm_sec == 60;
518 sec_adjustment -= sec;
519 sec_adjustment += sec_requested;
520 if (INT_ADD_WRAPV (t, sec_adjustment, &t)
de20b81a
PE
521 || ! (mktime_min <= t && t <= mktime_max))
522 {
523 __set_errno (EOVERFLOW);
524 return -1;
525 }
526 if (! convert_time (convert, t, &tm))
78575a84
UD
527 return -1;
528 }
529
80fd7387
RM
530 *tp = tm;
531 return t;
532}
533
8e6fd2bd 534#endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_INTERNAL */
eda78eec 535
8e6fd2bd 536#if defined _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS
eda78eec
UD
537
538/* Convert *TP to a time_t value. */
539time_t
85e07670 540mktime (struct tm *tp)
eda78eec 541{
eda78eec 542 /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
6226efbd 543 time zone names contained in the external variable 'tzname' shall
eda78eec
UD
544 be set as if the tzset() function had been called. */
545 __tzset ();
eda78eec 546
8e6fd2bd
PE
547# if defined _LIBC || NEED_MKTIME_WORKING
548 static mktime_offset_t localtime_offset;
7683e140 549 return __mktime_internal (tp, __localtime_r, &localtime_offset);
8e6fd2bd
PE
550# else
551# undef mktime
552 return mktime (tp);
553# endif
eda78eec 554}
8e6fd2bd 555#endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS */
eda78eec 556
80fd7387
RM
557#ifdef weak_alias
558weak_alias (mktime, timelocal)
28f540f4 559#endif
c5598d47
RM
560
561#ifdef _LIBC
562libc_hidden_def (mktime)
563libc_hidden_weak (timelocal)
564#endif
80fd7387 565\f
8e6fd2bd 566#if DEBUG_MKTIME
28f540f4 567
80fd7387 568static int
85e07670 569not_equal_tm (const struct tm *a, const struct tm *b)
80fd7387
RM
570{
571 return ((a->tm_sec ^ b->tm_sec)
572 | (a->tm_min ^ b->tm_min)
573 | (a->tm_hour ^ b->tm_hour)
574 | (a->tm_mday ^ b->tm_mday)
575 | (a->tm_mon ^ b->tm_mon)
576 | (a->tm_year ^ b->tm_year)
80fd7387 577 | (a->tm_yday ^ b->tm_yday)
ce73d683 578 | isdst_differ (a->tm_isdst, b->tm_isdst));
80fd7387 579}
28f540f4 580
80fd7387 581static void
85e07670 582print_tm (const struct tm *tp)
80fd7387 583{
fe0ec73e
UD
584 if (tp)
585 printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
586 tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
587 tp->tm_hour, tp->tm_min, tp->tm_sec,
588 tp->tm_yday, tp->tm_wday, tp->tm_isdst);
589 else
590 printf ("0");
80fd7387 591}
28f540f4 592
80fd7387 593static int
85e07670 594check_result (time_t tk, struct tm tmk, time_t tl, const struct tm *lt)
80fd7387 595{
fe0ec73e 596 if (tk != tl || !lt || not_equal_tm (&tmk, lt))
80fd7387
RM
597 {
598 printf ("mktime (");
fe0ec73e 599 print_tm (lt);
85e07670
RM
600 printf (")\nyields (");
601 print_tm (&tmk);
602 printf (") == %ld, should be %ld\n", (long int) tk, (long int) tl);
80fd7387
RM
603 return 1;
604 }
605
606 return 0;
607}
28f540f4 608
80fd7387 609int
85e07670 610main (int argc, char **argv)
80fd7387
RM
611{
612 int status = 0;
613 struct tm tm, tmk, tml;
fe0ec73e 614 struct tm *lt;
85e07670 615 time_t tk, tl, tl1;
80fd7387
RM
616 char trailer;
617
8e6fd2bd
PE
618 /* Sanity check, plus call tzset. */
619 tl = 0;
620 if (! localtime (&tl))
621 {
622 printf ("localtime (0) fails\n");
623 status = 1;
624 }
625
80fd7387
RM
626 if ((argc == 3 || argc == 4)
627 && (sscanf (argv[1], "%d-%d-%d%c",
628 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
629 == 3)
630 && (sscanf (argv[2], "%d:%d:%d%c",
631 &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
632 == 3))
633 {
634 tm.tm_year -= TM_YEAR_BASE;
635 tm.tm_mon--;
636 tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
637 tmk = tm;
638 tl = mktime (&tmk);
8e6fd2bd 639 lt = localtime_r (&tl, &tml);
85e07670 640 printf ("mktime returns %ld == ", (long int) tl);
80fd7387
RM
641 print_tm (&tmk);
642 printf ("\n");
fe0ec73e 643 status = check_result (tl, tmk, tl, lt);
80fd7387
RM
644 }
645 else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
646 {
647 time_t from = atol (argv[1]);
648 time_t by = atol (argv[2]);
649 time_t to = atol (argv[3]);
28f540f4 650
80fd7387 651 if (argc == 4)
85e07670 652 for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1)
80fd7387 653 {
8e6fd2bd 654 lt = localtime_r (&tl, &tml);
fe0ec73e
UD
655 if (lt)
656 {
8e6fd2bd 657 tmk = tml;
fe0ec73e 658 tk = mktime (&tmk);
85e07670 659 status |= check_result (tk, tmk, tl, &tml);
fe0ec73e
UD
660 }
661 else
662 {
8e6fd2bd 663 printf ("localtime_r (%ld) yields 0\n", (long int) tl);
fe0ec73e
UD
664 status = 1;
665 }
85e07670
RM
666 tl1 = tl + by;
667 if ((tl1 < tl) != (by < 0))
668 break;
80fd7387
RM
669 }
670 else
85e07670 671 for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1)
80fd7387
RM
672 {
673 /* Null benchmark. */
8e6fd2bd 674 lt = localtime_r (&tl, &tml);
fe0ec73e
UD
675 if (lt)
676 {
8e6fd2bd 677 tmk = tml;
fe0ec73e 678 tk = tl;
85e07670 679 status |= check_result (tk, tmk, tl, &tml);
fe0ec73e
UD
680 }
681 else
682 {
8e6fd2bd 683 printf ("localtime_r (%ld) yields 0\n", (long int) tl);
fe0ec73e
UD
684 status = 1;
685 }
85e07670
RM
686 tl1 = tl + by;
687 if ((tl1 < tl) != (by < 0))
688 break;
80fd7387
RM
689 }
690 }
691 else
692 printf ("Usage:\
693\t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
694\t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
695\t%s FROM BY TO - # Do not test those values (for benchmark).\n",
696 argv[0], argv[0], argv[0]);
697
698 return status;
28f540f4 699}
28f540f4 700
f2b3078e 701#endif /* DEBUG_MKTIME */
28f540f4
RM
702\f
703/*
704Local Variables:
f2b3078e 705compile-command: "gcc -DDEBUG_MKTIME -I. -Wall -W -O2 -g mktime.c -o mktime"
28f540f4
RM
706End:
707*/