186d7f5d3SJohn Marino /* Convert a `struct tm' to a time_t value.
286d7f5d3SJohn Marino Copyright (C) 1993-1999, 2002-2004, 2005 Free Software Foundation, Inc.
386d7f5d3SJohn Marino This file is part of the GNU C Library.
486d7f5d3SJohn Marino Contributed by Paul Eggert (eggert@twinsun.com).
586d7f5d3SJohn Marino
686d7f5d3SJohn Marino This program is free software; you can redistribute it and/or modify
786d7f5d3SJohn Marino it under the terms of the GNU General Public License as published by
886d7f5d3SJohn Marino the Free Software Foundation; either version 2, or (at your option)
986d7f5d3SJohn Marino any later version.
1086d7f5d3SJohn Marino
1186d7f5d3SJohn Marino This program is distributed in the hope that it will be useful,
1286d7f5d3SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
1386d7f5d3SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1486d7f5d3SJohn Marino GNU General Public License for more details.
1586d7f5d3SJohn Marino
1686d7f5d3SJohn Marino You should have received a copy of the GNU General Public License along
1786d7f5d3SJohn Marino with this program; if not, write to the Free Software Foundation,
1886d7f5d3SJohn Marino Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
1986d7f5d3SJohn Marino
2086d7f5d3SJohn Marino /* Define this to have a standalone program to test this implementation of
2186d7f5d3SJohn Marino mktime. */
2286d7f5d3SJohn Marino /* #define DEBUG 1 */
2386d7f5d3SJohn Marino
2486d7f5d3SJohn Marino #ifdef HAVE_CONFIG_H
2586d7f5d3SJohn Marino # include <config.h>
2686d7f5d3SJohn Marino #endif
2786d7f5d3SJohn Marino
2886d7f5d3SJohn Marino /* Assume that leap seconds are possible, unless told otherwise.
2986d7f5d3SJohn Marino If the host has a `zic' command with a `-L leapsecondfilename' option,
3086d7f5d3SJohn Marino then it supports leap seconds; otherwise it probably doesn't. */
3186d7f5d3SJohn Marino #ifndef LEAP_SECONDS_POSSIBLE
3286d7f5d3SJohn Marino # define LEAP_SECONDS_POSSIBLE 1
3386d7f5d3SJohn Marino #endif
3486d7f5d3SJohn Marino
3586d7f5d3SJohn Marino #include <sys/types.h> /* Some systems define `time_t' here. */
3686d7f5d3SJohn Marino #include <time.h>
3786d7f5d3SJohn Marino
3886d7f5d3SJohn Marino #include <limits.h>
3986d7f5d3SJohn Marino
4086d7f5d3SJohn Marino #include <string.h> /* For the real memcpy prototype. */
4186d7f5d3SJohn Marino
4286d7f5d3SJohn Marino #if DEBUG
4386d7f5d3SJohn Marino # include <stdio.h>
4486d7f5d3SJohn Marino # include <stdlib.h>
4586d7f5d3SJohn Marino /* Make it work even if the system's libc has its own mktime routine. */
4686d7f5d3SJohn Marino # define mktime my_mktime
4786d7f5d3SJohn Marino #endif /* DEBUG */
4886d7f5d3SJohn Marino
4986d7f5d3SJohn Marino /* Shift A right by B bits portably, by dividing A by 2**B and
5086d7f5d3SJohn Marino truncating towards minus infinity. A and B should be free of side
5186d7f5d3SJohn Marino effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
5286d7f5d3SJohn Marino INT_BITS is the number of useful bits in an int. GNU code can
5386d7f5d3SJohn Marino assume that INT_BITS is at least 32.
5486d7f5d3SJohn Marino
5586d7f5d3SJohn Marino ISO C99 says that A >> B is implementation-defined if A < 0. Some
5686d7f5d3SJohn Marino implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
5786d7f5d3SJohn Marino right in the usual way when A < 0, so SHR falls back on division if
5886d7f5d3SJohn Marino ordinary A >> B doesn't seem to be the usual signed shift. */
5986d7f5d3SJohn Marino #define SHR(a, b) \
6086d7f5d3SJohn Marino (-1 >> 1 == -1 \
6186d7f5d3SJohn Marino ? (a) >> (b) \
6286d7f5d3SJohn Marino : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
6386d7f5d3SJohn Marino
6486d7f5d3SJohn Marino /* The extra casts in the following macros work around compiler bugs,
6586d7f5d3SJohn Marino e.g., in Cray C 5.0.3.0. */
6686d7f5d3SJohn Marino
6786d7f5d3SJohn Marino /* True if the arithmetic type T is an integer type. bool counts as
6886d7f5d3SJohn Marino an integer. */
6986d7f5d3SJohn Marino #define TYPE_IS_INTEGER(t) ((t) 1.5 == 1)
7086d7f5d3SJohn Marino
7186d7f5d3SJohn Marino /* True if negative values of the signed integer type T use two's
7286d7f5d3SJohn Marino complement, ones' complement, or signed magnitude representation,
7386d7f5d3SJohn Marino respectively. Much GNU code assumes two's complement, but some
7486d7f5d3SJohn Marino people like to be portable to all possible C hosts. */
7586d7f5d3SJohn Marino #define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1)
7686d7f5d3SJohn Marino #define TYPE_ONES_COMPLEMENT(t) ((t) ~ (t) 0 == 0)
7786d7f5d3SJohn Marino #define TYPE_SIGNED_MAGNITUDE(t) ((t) ~ (t) 0 < (t) -1)
7886d7f5d3SJohn Marino
7986d7f5d3SJohn Marino /* True if the arithmetic type T is signed. */
8086d7f5d3SJohn Marino #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
8186d7f5d3SJohn Marino
8286d7f5d3SJohn Marino /* The maximum and minimum values for the integer type T. These
8386d7f5d3SJohn Marino macros have undefined behavior if T is signed and has padding bits.
8486d7f5d3SJohn Marino If this is a problem for you, please let us know how to fix it for
8586d7f5d3SJohn Marino your host. */
8686d7f5d3SJohn Marino #define TYPE_MINIMUM(t) \
8786d7f5d3SJohn Marino ((t) (! TYPE_SIGNED (t) \
8886d7f5d3SJohn Marino ? (t) 0 \
8986d7f5d3SJohn Marino : TYPE_SIGNED_MAGNITUDE (t) \
9086d7f5d3SJohn Marino ? ~ (t) 0 \
9186d7f5d3SJohn Marino : ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1)))
9286d7f5d3SJohn Marino #define TYPE_MAXIMUM(t) \
9386d7f5d3SJohn Marino ((t) (! TYPE_SIGNED (t) \
9486d7f5d3SJohn Marino ? (t) -1 \
9586d7f5d3SJohn Marino : ~ (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))))
9686d7f5d3SJohn Marino
9786d7f5d3SJohn Marino #ifndef TIME_T_MIN
9886d7f5d3SJohn Marino # define TIME_T_MIN TYPE_MINIMUM (time_t)
9986d7f5d3SJohn Marino #endif
10086d7f5d3SJohn Marino #ifndef TIME_T_MAX
10186d7f5d3SJohn Marino # define TIME_T_MAX TYPE_MAXIMUM (time_t)
10286d7f5d3SJohn Marino #endif
10386d7f5d3SJohn Marino #define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1)
10486d7f5d3SJohn Marino
10586d7f5d3SJohn Marino /* Verify a requirement at compile-time (unlike assert, which is runtime). */
10686d7f5d3SJohn Marino #define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
10786d7f5d3SJohn Marino
10886d7f5d3SJohn Marino verify (time_t_is_integer, TYPE_IS_INTEGER (time_t));
10986d7f5d3SJohn Marino verify (twos_complement_arithmetic, TYPE_TWOS_COMPLEMENT (int));
11086d7f5d3SJohn Marino /* The code also assumes that signed integer overflow silently wraps
11186d7f5d3SJohn Marino around, but this assumption can't be stated without causing a
11286d7f5d3SJohn Marino diagnostic on some hosts. */
11386d7f5d3SJohn Marino
11486d7f5d3SJohn Marino #define EPOCH_YEAR 1970
11586d7f5d3SJohn Marino #define TM_YEAR_BASE 1900
11686d7f5d3SJohn Marino verify (base_year_is_a_multiple_of_100, TM_YEAR_BASE % 100 == 0);
11786d7f5d3SJohn Marino
11886d7f5d3SJohn Marino /* Return 1 if YEAR + TM_YEAR_BASE is a leap year. */
11986d7f5d3SJohn Marino static inline int
leapyear(long int year)12086d7f5d3SJohn Marino leapyear (long int year)
12186d7f5d3SJohn Marino {
12286d7f5d3SJohn Marino /* Don't add YEAR to TM_YEAR_BASE, as that might overflow.
12386d7f5d3SJohn Marino Also, work even if YEAR is negative. */
12486d7f5d3SJohn Marino return
12586d7f5d3SJohn Marino ((year & 3) == 0
12686d7f5d3SJohn Marino && (year % 100 != 0
12786d7f5d3SJohn Marino || ((year / 100) & 3) == (- (TM_YEAR_BASE / 100) & 3)));
12886d7f5d3SJohn Marino }
12986d7f5d3SJohn Marino
13086d7f5d3SJohn Marino /* How many days come before each month (0-12). */
13186d7f5d3SJohn Marino #ifndef _LIBC
13286d7f5d3SJohn Marino static
13386d7f5d3SJohn Marino #endif
13486d7f5d3SJohn Marino const unsigned short int __mon_yday[2][13] =
13586d7f5d3SJohn Marino {
13686d7f5d3SJohn Marino /* Normal years. */
13786d7f5d3SJohn Marino { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
13886d7f5d3SJohn Marino /* Leap years. */
13986d7f5d3SJohn Marino { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
14086d7f5d3SJohn Marino };
14186d7f5d3SJohn Marino
14286d7f5d3SJohn Marino
14386d7f5d3SJohn Marino #ifndef _LIBC
14486d7f5d3SJohn Marino /* Portable standalone applications should supply a "time_r.h" that
14586d7f5d3SJohn Marino declares a POSIX-compliant localtime_r, for the benefit of older
14686d7f5d3SJohn Marino implementations that lack localtime_r or have a nonstandard one.
14786d7f5d3SJohn Marino See the gnulib time_r module for one way to implement this. */
14886d7f5d3SJohn Marino # include "time_r.h"
14986d7f5d3SJohn Marino # undef __localtime_r
15086d7f5d3SJohn Marino # define __localtime_r localtime_r
15186d7f5d3SJohn Marino # define __mktime_internal mktime_internal
15286d7f5d3SJohn Marino #endif
15386d7f5d3SJohn Marino
15486d7f5d3SJohn Marino /* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) -
15586d7f5d3SJohn Marino (YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks
15686d7f5d3SJohn Marino were not adjusted between the time stamps.
15786d7f5d3SJohn Marino
15886d7f5d3SJohn Marino The YEAR values uses the same numbering as TP->tm_year. Values
15986d7f5d3SJohn Marino need not be in the usual range. However, YEAR1 must not be less
16086d7f5d3SJohn Marino than 2 * INT_MIN or greater than 2 * INT_MAX.
16186d7f5d3SJohn Marino
16286d7f5d3SJohn Marino The result may overflow. It is the caller's responsibility to
16386d7f5d3SJohn Marino detect overflow. */
16486d7f5d3SJohn Marino
16586d7f5d3SJohn Marino static inline time_t
ydhms_diff(long int year1,long int yday1,int hour1,int min1,int sec1,int year0,int yday0,int hour0,int min0,int sec0)16686d7f5d3SJohn Marino ydhms_diff (long int year1, long int yday1, int hour1, int min1, int sec1,
16786d7f5d3SJohn Marino int year0, int yday0, int hour0, int min0, int sec0)
16886d7f5d3SJohn Marino {
16986d7f5d3SJohn Marino verify (C99_integer_division, -1 / 2 == 0);
17086d7f5d3SJohn Marino verify (long_int_year_and_yday_are_wide_enough,
17186d7f5d3SJohn Marino INT_MAX <= LONG_MAX / 2 || TIME_T_MAX <= UINT_MAX);
17286d7f5d3SJohn Marino
17386d7f5d3SJohn Marino /* Compute intervening leap days correctly even if year is negative.
17486d7f5d3SJohn Marino Take care to avoid integer overflow here. */
17586d7f5d3SJohn Marino int a4 = SHR (year1, 2) + SHR (TM_YEAR_BASE, 2) - ! (year1 & 3);
17686d7f5d3SJohn Marino int b4 = SHR (year0, 2) + SHR (TM_YEAR_BASE, 2) - ! (year0 & 3);
17786d7f5d3SJohn Marino int a100 = a4 / 25 - (a4 % 25 < 0);
17886d7f5d3SJohn Marino int b100 = b4 / 25 - (b4 % 25 < 0);
17986d7f5d3SJohn Marino int a400 = SHR (a100, 2);
18086d7f5d3SJohn Marino int b400 = SHR (b100, 2);
18186d7f5d3SJohn Marino int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
18286d7f5d3SJohn Marino
18386d7f5d3SJohn Marino /* Compute the desired time in time_t precision. Overflow might
18486d7f5d3SJohn Marino occur here. */
18586d7f5d3SJohn Marino time_t tyear1 = year1;
18686d7f5d3SJohn Marino time_t years = tyear1 - year0;
18786d7f5d3SJohn Marino time_t days = 365 * years + yday1 - yday0 + intervening_leap_days;
18886d7f5d3SJohn Marino time_t hours = 24 * days + hour1 - hour0;
18986d7f5d3SJohn Marino time_t minutes = 60 * hours + min1 - min0;
19086d7f5d3SJohn Marino time_t seconds = 60 * minutes + sec1 - sec0;
19186d7f5d3SJohn Marino return seconds;
19286d7f5d3SJohn Marino }
19386d7f5d3SJohn Marino
19486d7f5d3SJohn Marino
19586d7f5d3SJohn Marino /* Return a time_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC),
19686d7f5d3SJohn Marino assuming that *T corresponds to *TP and that no clock adjustments
19786d7f5d3SJohn Marino occurred between *TP and the desired time.
19886d7f5d3SJohn Marino If TP is null, return a value not equal to *T; this avoids false matches.
19986d7f5d3SJohn Marino If overflow occurs, yield the minimal or maximal value, except do not
20086d7f5d3SJohn Marino yield a value equal to *T. */
20186d7f5d3SJohn Marino static time_t
guess_time_tm(long int year,long int yday,int hour,int min,int sec,const time_t * t,const struct tm * tp)20286d7f5d3SJohn Marino guess_time_tm (long int year, long int yday, int hour, int min, int sec,
20386d7f5d3SJohn Marino const time_t *t, const struct tm *tp)
20486d7f5d3SJohn Marino {
20586d7f5d3SJohn Marino if (tp)
20686d7f5d3SJohn Marino {
20786d7f5d3SJohn Marino time_t d = ydhms_diff (year, yday, hour, min, sec,
20886d7f5d3SJohn Marino tp->tm_year, tp->tm_yday,
20986d7f5d3SJohn Marino tp->tm_hour, tp->tm_min, tp->tm_sec);
21086d7f5d3SJohn Marino time_t t1 = *t + d;
21186d7f5d3SJohn Marino if ((t1 < *t) == (TYPE_SIGNED (time_t) ? d < 0 : TIME_T_MAX / 2 < d))
21286d7f5d3SJohn Marino return t1;
21386d7f5d3SJohn Marino }
21486d7f5d3SJohn Marino
21586d7f5d3SJohn Marino /* Overflow occurred one way or another. Return the nearest result
21686d7f5d3SJohn Marino that is actually in range, except don't report a zero difference
21786d7f5d3SJohn Marino if the actual difference is nonzero, as that would cause a false
21886d7f5d3SJohn Marino match. */
21986d7f5d3SJohn Marino return (*t < TIME_T_MIDPOINT
22086d7f5d3SJohn Marino ? TIME_T_MIN + (*t == TIME_T_MIN)
22186d7f5d3SJohn Marino : TIME_T_MAX - (*t == TIME_T_MAX));
22286d7f5d3SJohn Marino }
22386d7f5d3SJohn Marino
22486d7f5d3SJohn Marino /* Use CONVERT to convert *T to a broken down time in *TP.
22586d7f5d3SJohn Marino If *T is out of range for conversion, adjust it so that
22686d7f5d3SJohn Marino it is the nearest in-range value and then convert that. */
22786d7f5d3SJohn Marino static struct tm *
ranged_convert(struct tm * (* convert)(const time_t *,struct tm *),time_t * t,struct tm * tp)22886d7f5d3SJohn Marino ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
22986d7f5d3SJohn Marino time_t *t, struct tm *tp)
23086d7f5d3SJohn Marino {
23186d7f5d3SJohn Marino struct tm *r = convert (t, tp);
23286d7f5d3SJohn Marino
23386d7f5d3SJohn Marino if (!r && *t)
23486d7f5d3SJohn Marino {
23586d7f5d3SJohn Marino time_t bad = *t;
23686d7f5d3SJohn Marino time_t ok = 0;
23786d7f5d3SJohn Marino
23886d7f5d3SJohn Marino /* BAD is a known unconvertible time_t, and OK is a known good one.
23986d7f5d3SJohn Marino Use binary search to narrow the range between BAD and OK until
24086d7f5d3SJohn Marino they differ by 1. */
24186d7f5d3SJohn Marino while (bad != ok + (bad < 0 ? -1 : 1))
24286d7f5d3SJohn Marino {
24386d7f5d3SJohn Marino time_t mid = *t = (bad < 0
24486d7f5d3SJohn Marino ? bad + ((ok - bad) >> 1)
24586d7f5d3SJohn Marino : ok + ((bad - ok) >> 1));
24686d7f5d3SJohn Marino r = convert (t, tp);
24786d7f5d3SJohn Marino if (r)
24886d7f5d3SJohn Marino ok = mid;
24986d7f5d3SJohn Marino else
25086d7f5d3SJohn Marino bad = mid;
25186d7f5d3SJohn Marino }
25286d7f5d3SJohn Marino
25386d7f5d3SJohn Marino if (!r && ok)
25486d7f5d3SJohn Marino {
25586d7f5d3SJohn Marino /* The last conversion attempt failed;
25686d7f5d3SJohn Marino revert to the most recent successful attempt. */
25786d7f5d3SJohn Marino *t = ok;
25886d7f5d3SJohn Marino r = convert (t, tp);
25986d7f5d3SJohn Marino }
26086d7f5d3SJohn Marino }
26186d7f5d3SJohn Marino
26286d7f5d3SJohn Marino return r;
26386d7f5d3SJohn Marino }
26486d7f5d3SJohn Marino
26586d7f5d3SJohn Marino
26686d7f5d3SJohn Marino /* Convert *TP to a time_t value, inverting
26786d7f5d3SJohn Marino the monotonic and mostly-unit-linear conversion function CONVERT.
26886d7f5d3SJohn Marino Use *OFFSET to keep track of a guess at the offset of the result,
26986d7f5d3SJohn Marino compared to what the result would be for UTC without leap seconds.
27086d7f5d3SJohn Marino If *OFFSET's guess is correct, only one CONVERT call is needed.
27186d7f5d3SJohn Marino This function is external because it is used also by timegm.c. */
27286d7f5d3SJohn Marino time_t
__mktime_internal(struct tm * tp,struct tm * (* convert)(const time_t *,struct tm *),time_t * offset)27386d7f5d3SJohn Marino __mktime_internal (struct tm *tp,
27486d7f5d3SJohn Marino struct tm *(*convert) (const time_t *, struct tm *),
27586d7f5d3SJohn Marino time_t *offset)
27686d7f5d3SJohn Marino {
27786d7f5d3SJohn Marino time_t t, gt, t0, t1, t2;
27886d7f5d3SJohn Marino struct tm tm;
27986d7f5d3SJohn Marino
28086d7f5d3SJohn Marino /* The maximum number of probes (calls to CONVERT) should be enough
28186d7f5d3SJohn Marino to handle any combinations of time zone rule changes, solar time,
28286d7f5d3SJohn Marino leap seconds, and oscillations around a spring-forward gap.
28386d7f5d3SJohn Marino POSIX.1 prohibits leap seconds, but some hosts have them anyway. */
28486d7f5d3SJohn Marino int remaining_probes = 6;
28586d7f5d3SJohn Marino
28686d7f5d3SJohn Marino /* Time requested. Copy it in case CONVERT modifies *TP; this can
28786d7f5d3SJohn Marino occur if TP is localtime's returned value and CONVERT is localtime. */
28886d7f5d3SJohn Marino int sec = tp->tm_sec;
28986d7f5d3SJohn Marino int min = tp->tm_min;
29086d7f5d3SJohn Marino int hour = tp->tm_hour;
29186d7f5d3SJohn Marino int mday = tp->tm_mday;
29286d7f5d3SJohn Marino int mon = tp->tm_mon;
29386d7f5d3SJohn Marino int year_requested = tp->tm_year;
29486d7f5d3SJohn Marino int isdst = tp->tm_isdst;
29586d7f5d3SJohn Marino
29686d7f5d3SJohn Marino /* 1 if the previous probe was DST. */
29786d7f5d3SJohn Marino int dst2;
29886d7f5d3SJohn Marino
29986d7f5d3SJohn Marino /* Ensure that mon is in range, and set year accordingly. */
30086d7f5d3SJohn Marino int mon_remainder = mon % 12;
30186d7f5d3SJohn Marino int negative_mon_remainder = mon_remainder < 0;
30286d7f5d3SJohn Marino int mon_years = mon / 12 - negative_mon_remainder;
30386d7f5d3SJohn Marino long int lyear_requested = year_requested;
30486d7f5d3SJohn Marino long int year = lyear_requested + mon_years;
30586d7f5d3SJohn Marino
30686d7f5d3SJohn Marino /* The other values need not be in range:
30786d7f5d3SJohn Marino the remaining code handles minor overflows correctly,
30886d7f5d3SJohn Marino assuming int and time_t arithmetic wraps around.
30986d7f5d3SJohn Marino Major overflows are caught at the end. */
31086d7f5d3SJohn Marino
31186d7f5d3SJohn Marino /* Calculate day of year from year, month, and day of month.
31286d7f5d3SJohn Marino The result need not be in range. */
31386d7f5d3SJohn Marino int mon_yday = ((__mon_yday[leapyear (year)]
31486d7f5d3SJohn Marino [mon_remainder + 12 * negative_mon_remainder])
31586d7f5d3SJohn Marino - 1);
31686d7f5d3SJohn Marino long int lmday = mday;
31786d7f5d3SJohn Marino long int yday = mon_yday + lmday;
31886d7f5d3SJohn Marino
31986d7f5d3SJohn Marino time_t guessed_offset = *offset;
32086d7f5d3SJohn Marino
32186d7f5d3SJohn Marino int sec_requested = sec;
32286d7f5d3SJohn Marino
32386d7f5d3SJohn Marino if (LEAP_SECONDS_POSSIBLE)
32486d7f5d3SJohn Marino {
32586d7f5d3SJohn Marino /* Handle out-of-range seconds specially,
32686d7f5d3SJohn Marino since ydhms_tm_diff assumes every minute has 60 seconds. */
32786d7f5d3SJohn Marino if (sec < 0)
32886d7f5d3SJohn Marino sec = 0;
32986d7f5d3SJohn Marino if (59 < sec)
33086d7f5d3SJohn Marino sec = 59;
33186d7f5d3SJohn Marino }
33286d7f5d3SJohn Marino
33386d7f5d3SJohn Marino /* Invert CONVERT by probing. First assume the same offset as last
33486d7f5d3SJohn Marino time. */
33586d7f5d3SJohn Marino
33686d7f5d3SJohn Marino t0 = ydhms_diff (year, yday, hour, min, sec,
33786d7f5d3SJohn Marino EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset);
33886d7f5d3SJohn Marino
33986d7f5d3SJohn Marino if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
34086d7f5d3SJohn Marino {
34186d7f5d3SJohn Marino /* time_t isn't large enough to rule out overflows, so check
34286d7f5d3SJohn Marino for major overflows. A gross check suffices, since if t0
34386d7f5d3SJohn Marino has overflowed, it is off by a multiple of TIME_T_MAX -
34486d7f5d3SJohn Marino TIME_T_MIN + 1. So ignore any component of the difference
34586d7f5d3SJohn Marino that is bounded by a small value. */
34686d7f5d3SJohn Marino
34786d7f5d3SJohn Marino /* Approximate log base 2 of the number of time units per
34886d7f5d3SJohn Marino biennium. A biennium is 2 years; use this unit instead of
34986d7f5d3SJohn Marino years to avoid integer overflow. For example, 2 average
35086d7f5d3SJohn Marino Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds,
35186d7f5d3SJohn Marino which is 63113904 seconds, and rint (log2 (63113904)) is
35286d7f5d3SJohn Marino 26. */
35386d7f5d3SJohn Marino int ALOG2_SECONDS_PER_BIENNIUM = 26;
35486d7f5d3SJohn Marino int ALOG2_MINUTES_PER_BIENNIUM = 20;
35586d7f5d3SJohn Marino int ALOG2_HOURS_PER_BIENNIUM = 14;
35686d7f5d3SJohn Marino int ALOG2_DAYS_PER_BIENNIUM = 10;
35786d7f5d3SJohn Marino int LOG2_YEARS_PER_BIENNIUM = 1;
35886d7f5d3SJohn Marino
35986d7f5d3SJohn Marino int approx_requested_biennia =
36086d7f5d3SJohn Marino (SHR (year_requested, LOG2_YEARS_PER_BIENNIUM)
36186d7f5d3SJohn Marino - SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM)
36286d7f5d3SJohn Marino + SHR (mday, ALOG2_DAYS_PER_BIENNIUM)
36386d7f5d3SJohn Marino + SHR (hour, ALOG2_HOURS_PER_BIENNIUM)
36486d7f5d3SJohn Marino + SHR (min, ALOG2_MINUTES_PER_BIENNIUM)
36586d7f5d3SJohn Marino + (LEAP_SECONDS_POSSIBLE
36686d7f5d3SJohn Marino ? 0
36786d7f5d3SJohn Marino : SHR (sec, ALOG2_SECONDS_PER_BIENNIUM)));
36886d7f5d3SJohn Marino
36986d7f5d3SJohn Marino int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM);
37086d7f5d3SJohn Marino int diff = approx_biennia - approx_requested_biennia;
37186d7f5d3SJohn Marino int abs_diff = diff < 0 ? - diff : diff;
37286d7f5d3SJohn Marino
37386d7f5d3SJohn Marino /* IRIX 4.0.5 cc miscaculates TIME_T_MIN / 3: it erroneously
37486d7f5d3SJohn Marino gives a positive value of 715827882. Setting a variable
37586d7f5d3SJohn Marino first then doing math on it seems to work.
37686d7f5d3SJohn Marino (ghazi@caip.rutgers.edu) */
37786d7f5d3SJohn Marino time_t time_t_max = TIME_T_MAX;
37886d7f5d3SJohn Marino time_t time_t_min = TIME_T_MIN;
37986d7f5d3SJohn Marino time_t overflow_threshold =
38086d7f5d3SJohn Marino (time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM;
38186d7f5d3SJohn Marino
38286d7f5d3SJohn Marino if (overflow_threshold < abs_diff)
38386d7f5d3SJohn Marino {
38486d7f5d3SJohn Marino /* Overflow occurred. Try repairing it; this might work if
38586d7f5d3SJohn Marino the time zone offset is enough to undo the overflow. */
38686d7f5d3SJohn Marino time_t repaired_t0 = -1 - t0;
38786d7f5d3SJohn Marino approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM);
38886d7f5d3SJohn Marino diff = approx_biennia - approx_requested_biennia;
38986d7f5d3SJohn Marino abs_diff = diff < 0 ? - diff : diff;
39086d7f5d3SJohn Marino if (overflow_threshold < abs_diff)
39186d7f5d3SJohn Marino return -1;
39286d7f5d3SJohn Marino guessed_offset += repaired_t0 - t0;
39386d7f5d3SJohn Marino t0 = repaired_t0;
39486d7f5d3SJohn Marino }
39586d7f5d3SJohn Marino }
39686d7f5d3SJohn Marino
39786d7f5d3SJohn Marino /* Repeatedly use the error to improve the guess. */
39886d7f5d3SJohn Marino
39986d7f5d3SJohn Marino for (t = t1 = t2 = t0, dst2 = 0;
40086d7f5d3SJohn Marino (gt = guess_time_tm (year, yday, hour, min, sec, &t,
40186d7f5d3SJohn Marino ranged_convert (convert, &t, &tm)),
40286d7f5d3SJohn Marino t != gt);
40386d7f5d3SJohn Marino t1 = t2, t2 = t, t = gt, dst2 = tm.tm_isdst != 0)
40486d7f5d3SJohn Marino if (t == t1 && t != t2
40586d7f5d3SJohn Marino && (tm.tm_isdst < 0
40686d7f5d3SJohn Marino || (isdst < 0
40786d7f5d3SJohn Marino ? dst2 <= (tm.tm_isdst != 0)
40886d7f5d3SJohn Marino : (isdst != 0) != (tm.tm_isdst != 0))))
40986d7f5d3SJohn Marino /* We can't possibly find a match, as we are oscillating
41086d7f5d3SJohn Marino between two values. The requested time probably falls
41186d7f5d3SJohn Marino within a spring-forward gap of size GT - T. Follow the common
41286d7f5d3SJohn Marino practice in this case, which is to return a time that is GT - T
41386d7f5d3SJohn Marino away from the requested time, preferring a time whose
41486d7f5d3SJohn Marino tm_isdst differs from the requested value. (If no tm_isdst
41586d7f5d3SJohn Marino was requested and only one of the two values has a nonzero
41686d7f5d3SJohn Marino tm_isdst, prefer that value.) In practice, this is more
41786d7f5d3SJohn Marino useful than returning -1. */
41886d7f5d3SJohn Marino goto offset_found;
41986d7f5d3SJohn Marino else if (--remaining_probes == 0)
42086d7f5d3SJohn Marino return -1;
42186d7f5d3SJohn Marino
42286d7f5d3SJohn Marino /* We have a match. Check whether tm.tm_isdst has the requested
42386d7f5d3SJohn Marino value, if any. */
42486d7f5d3SJohn Marino if (isdst != tm.tm_isdst && 0 <= isdst && 0 <= tm.tm_isdst)
42586d7f5d3SJohn Marino {
42686d7f5d3SJohn Marino /* tm.tm_isdst has the wrong value. Look for a neighboring
42786d7f5d3SJohn Marino time with the right value, and use its UTC offset.
42886d7f5d3SJohn Marino
42986d7f5d3SJohn Marino Heuristic: probe the adjacent timestamps in both directions,
43086d7f5d3SJohn Marino looking for the desired isdst. This should work for all real
43186d7f5d3SJohn Marino time zone histories in the tz database. */
43286d7f5d3SJohn Marino
43386d7f5d3SJohn Marino /* Distance between probes when looking for a DST boundary. In
43486d7f5d3SJohn Marino tzdata2003a, the shortest period of DST is 601200 seconds
43586d7f5d3SJohn Marino (e.g., America/Recife starting 2000-10-08 01:00), and the
43686d7f5d3SJohn Marino shortest period of non-DST surrounded by DST is 694800
43786d7f5d3SJohn Marino seconds (Africa/Tunis starting 1943-04-17 01:00). Use the
43886d7f5d3SJohn Marino minimum of these two values, so we don't miss these short
43986d7f5d3SJohn Marino periods when probing. */
44086d7f5d3SJohn Marino int stride = 601200;
44186d7f5d3SJohn Marino
44286d7f5d3SJohn Marino /* The longest period of DST in tzdata2003a is 536454000 seconds
44386d7f5d3SJohn Marino (e.g., America/Jujuy starting 1946-10-01 01:00). The longest
44486d7f5d3SJohn Marino period of non-DST is much longer, but it makes no real sense
44586d7f5d3SJohn Marino to search for more than a year of non-DST, so use the DST
44686d7f5d3SJohn Marino max. */
44786d7f5d3SJohn Marino int duration_max = 536454000;
44886d7f5d3SJohn Marino
44986d7f5d3SJohn Marino /* Search in both directions, so the maximum distance is half
45086d7f5d3SJohn Marino the duration; add the stride to avoid off-by-1 problems. */
45186d7f5d3SJohn Marino int delta_bound = duration_max / 2 + stride;
45286d7f5d3SJohn Marino
45386d7f5d3SJohn Marino int delta, direction;
45486d7f5d3SJohn Marino
45586d7f5d3SJohn Marino for (delta = stride; delta < delta_bound; delta += stride)
45686d7f5d3SJohn Marino for (direction = -1; direction <= 1; direction += 2)
45786d7f5d3SJohn Marino {
45886d7f5d3SJohn Marino time_t ot = t + delta * direction;
45986d7f5d3SJohn Marino if ((ot < t) == (direction < 0))
46086d7f5d3SJohn Marino {
46186d7f5d3SJohn Marino struct tm otm;
46286d7f5d3SJohn Marino ranged_convert (convert, &ot, &otm);
46386d7f5d3SJohn Marino if (otm.tm_isdst == isdst)
46486d7f5d3SJohn Marino {
46586d7f5d3SJohn Marino /* We found the desired tm_isdst.
46686d7f5d3SJohn Marino Extrapolate back to the desired time. */
46786d7f5d3SJohn Marino t = guess_time_tm (year, yday, hour, min, sec, &ot, &otm);
46886d7f5d3SJohn Marino ranged_convert (convert, &t, &tm);
46986d7f5d3SJohn Marino goto offset_found;
47086d7f5d3SJohn Marino }
47186d7f5d3SJohn Marino }
47286d7f5d3SJohn Marino }
47386d7f5d3SJohn Marino }
47486d7f5d3SJohn Marino
47586d7f5d3SJohn Marino offset_found:
47686d7f5d3SJohn Marino *offset = guessed_offset + t - t0;
47786d7f5d3SJohn Marino
47886d7f5d3SJohn Marino if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec)
47986d7f5d3SJohn Marino {
48086d7f5d3SJohn Marino /* Adjust time to reflect the tm_sec requested, not the normalized value.
48186d7f5d3SJohn Marino Also, repair any damage from a false match due to a leap second. */
48286d7f5d3SJohn Marino int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec;
48386d7f5d3SJohn Marino t1 = t + sec_requested;
48486d7f5d3SJohn Marino t2 = t1 + sec_adjustment;
48586d7f5d3SJohn Marino if (((t1 < t) != (sec_requested < 0))
48686d7f5d3SJohn Marino | ((t2 < t1) != (sec_adjustment < 0))
48786d7f5d3SJohn Marino | ! convert (&t2, &tm))
48886d7f5d3SJohn Marino return -1;
48986d7f5d3SJohn Marino t = t2;
49086d7f5d3SJohn Marino }
49186d7f5d3SJohn Marino
49286d7f5d3SJohn Marino *tp = tm;
49386d7f5d3SJohn Marino return t;
49486d7f5d3SJohn Marino }
49586d7f5d3SJohn Marino
49686d7f5d3SJohn Marino
49786d7f5d3SJohn Marino /* FIXME: This should use a signed type wide enough to hold any UTC
49886d7f5d3SJohn Marino offset in seconds. 'int' should be good enough for GNU code. We
49986d7f5d3SJohn Marino can't fix this unilaterally though, as other modules invoke
50086d7f5d3SJohn Marino __mktime_internal. */
50186d7f5d3SJohn Marino static time_t localtime_offset;
50286d7f5d3SJohn Marino
50386d7f5d3SJohn Marino /* Convert *TP to a time_t value. */
50486d7f5d3SJohn Marino time_t
mktime(struct tm * tp)50586d7f5d3SJohn Marino mktime (struct tm *tp)
50686d7f5d3SJohn Marino {
50786d7f5d3SJohn Marino #ifdef _LIBC
50886d7f5d3SJohn Marino /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
50986d7f5d3SJohn Marino time zone names contained in the external variable `tzname' shall
51086d7f5d3SJohn Marino be set as if the tzset() function had been called. */
51186d7f5d3SJohn Marino __tzset ();
51286d7f5d3SJohn Marino #endif
51386d7f5d3SJohn Marino
51486d7f5d3SJohn Marino return __mktime_internal (tp, __localtime_r, &localtime_offset);
51586d7f5d3SJohn Marino }
51686d7f5d3SJohn Marino
51786d7f5d3SJohn Marino #ifdef weak_alias
weak_alias(mktime,timelocal)51886d7f5d3SJohn Marino weak_alias (mktime, timelocal)
51986d7f5d3SJohn Marino #endif
52086d7f5d3SJohn Marino
52186d7f5d3SJohn Marino #ifdef _LIBC
52286d7f5d3SJohn Marino libc_hidden_def (mktime)
52386d7f5d3SJohn Marino libc_hidden_weak (timelocal)
52486d7f5d3SJohn Marino #endif
52586d7f5d3SJohn Marino
52686d7f5d3SJohn Marino #if DEBUG
52786d7f5d3SJohn Marino
52886d7f5d3SJohn Marino static int
52986d7f5d3SJohn Marino not_equal_tm (const struct tm *a, const struct tm *b)
53086d7f5d3SJohn Marino {
53186d7f5d3SJohn Marino return ((a->tm_sec ^ b->tm_sec)
53286d7f5d3SJohn Marino | (a->tm_min ^ b->tm_min)
53386d7f5d3SJohn Marino | (a->tm_hour ^ b->tm_hour)
53486d7f5d3SJohn Marino | (a->tm_mday ^ b->tm_mday)
53586d7f5d3SJohn Marino | (a->tm_mon ^ b->tm_mon)
53686d7f5d3SJohn Marino | (a->tm_year ^ b->tm_year)
53786d7f5d3SJohn Marino | (a->tm_yday ^ b->tm_yday)
53886d7f5d3SJohn Marino | (a->tm_isdst ^ b->tm_isdst));
53986d7f5d3SJohn Marino }
54086d7f5d3SJohn Marino
54186d7f5d3SJohn Marino static void
print_tm(const struct tm * tp)54286d7f5d3SJohn Marino print_tm (const struct tm *tp)
54386d7f5d3SJohn Marino {
54486d7f5d3SJohn Marino if (tp)
54586d7f5d3SJohn Marino printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
54686d7f5d3SJohn Marino tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
54786d7f5d3SJohn Marino tp->tm_hour, tp->tm_min, tp->tm_sec,
54886d7f5d3SJohn Marino tp->tm_yday, tp->tm_wday, tp->tm_isdst);
54986d7f5d3SJohn Marino else
55086d7f5d3SJohn Marino printf ("0");
55186d7f5d3SJohn Marino }
55286d7f5d3SJohn Marino
55386d7f5d3SJohn Marino static int
check_result(time_t tk,struct tm tmk,time_t tl,const struct tm * lt)55486d7f5d3SJohn Marino check_result (time_t tk, struct tm tmk, time_t tl, const struct tm *lt)
55586d7f5d3SJohn Marino {
55686d7f5d3SJohn Marino if (tk != tl || !lt || not_equal_tm (&tmk, lt))
55786d7f5d3SJohn Marino {
55886d7f5d3SJohn Marino printf ("mktime (");
55986d7f5d3SJohn Marino print_tm (lt);
56086d7f5d3SJohn Marino printf (")\nyields (");
56186d7f5d3SJohn Marino print_tm (&tmk);
56286d7f5d3SJohn Marino printf (") == %ld, should be %ld\n", (long int) tk, (long int) tl);
56386d7f5d3SJohn Marino return 1;
56486d7f5d3SJohn Marino }
56586d7f5d3SJohn Marino
56686d7f5d3SJohn Marino return 0;
56786d7f5d3SJohn Marino }
56886d7f5d3SJohn Marino
56986d7f5d3SJohn Marino int
main(int argc,char ** argv)57086d7f5d3SJohn Marino main (int argc, char **argv)
57186d7f5d3SJohn Marino {
57286d7f5d3SJohn Marino int status = 0;
57386d7f5d3SJohn Marino struct tm tm, tmk, tml;
57486d7f5d3SJohn Marino struct tm *lt;
57586d7f5d3SJohn Marino time_t tk, tl, tl1;
57686d7f5d3SJohn Marino char trailer;
57786d7f5d3SJohn Marino
57886d7f5d3SJohn Marino if ((argc == 3 || argc == 4)
57986d7f5d3SJohn Marino && (sscanf (argv[1], "%d-%d-%d%c",
58086d7f5d3SJohn Marino &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
58186d7f5d3SJohn Marino == 3)
58286d7f5d3SJohn Marino && (sscanf (argv[2], "%d:%d:%d%c",
58386d7f5d3SJohn Marino &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
58486d7f5d3SJohn Marino == 3))
58586d7f5d3SJohn Marino {
58686d7f5d3SJohn Marino tm.tm_year -= TM_YEAR_BASE;
58786d7f5d3SJohn Marino tm.tm_mon--;
58886d7f5d3SJohn Marino tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
58986d7f5d3SJohn Marino tmk = tm;
59086d7f5d3SJohn Marino tl = mktime (&tmk);
59186d7f5d3SJohn Marino lt = localtime (&tl);
59286d7f5d3SJohn Marino if (lt)
59386d7f5d3SJohn Marino {
59486d7f5d3SJohn Marino tml = *lt;
59586d7f5d3SJohn Marino lt = &tml;
59686d7f5d3SJohn Marino }
59786d7f5d3SJohn Marino printf ("mktime returns %ld == ", (long int) tl);
59886d7f5d3SJohn Marino print_tm (&tmk);
59986d7f5d3SJohn Marino printf ("\n");
60086d7f5d3SJohn Marino status = check_result (tl, tmk, tl, lt);
60186d7f5d3SJohn Marino }
60286d7f5d3SJohn Marino else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
60386d7f5d3SJohn Marino {
60486d7f5d3SJohn Marino time_t from = atol (argv[1]);
60586d7f5d3SJohn Marino time_t by = atol (argv[2]);
60686d7f5d3SJohn Marino time_t to = atol (argv[3]);
60786d7f5d3SJohn Marino
60886d7f5d3SJohn Marino if (argc == 4)
60986d7f5d3SJohn Marino for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1)
61086d7f5d3SJohn Marino {
61186d7f5d3SJohn Marino lt = localtime (&tl);
61286d7f5d3SJohn Marino if (lt)
61386d7f5d3SJohn Marino {
61486d7f5d3SJohn Marino tmk = tml = *lt;
61586d7f5d3SJohn Marino tk = mktime (&tmk);
61686d7f5d3SJohn Marino status |= check_result (tk, tmk, tl, &tml);
61786d7f5d3SJohn Marino }
61886d7f5d3SJohn Marino else
61986d7f5d3SJohn Marino {
62086d7f5d3SJohn Marino printf ("localtime (%ld) yields 0\n", (long int) tl);
62186d7f5d3SJohn Marino status = 1;
62286d7f5d3SJohn Marino }
62386d7f5d3SJohn Marino tl1 = tl + by;
62486d7f5d3SJohn Marino if ((tl1 < tl) != (by < 0))
62586d7f5d3SJohn Marino break;
62686d7f5d3SJohn Marino }
62786d7f5d3SJohn Marino else
62886d7f5d3SJohn Marino for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1)
62986d7f5d3SJohn Marino {
63086d7f5d3SJohn Marino /* Null benchmark. */
63186d7f5d3SJohn Marino lt = localtime (&tl);
63286d7f5d3SJohn Marino if (lt)
63386d7f5d3SJohn Marino {
63486d7f5d3SJohn Marino tmk = tml = *lt;
63586d7f5d3SJohn Marino tk = tl;
63686d7f5d3SJohn Marino status |= check_result (tk, tmk, tl, &tml);
63786d7f5d3SJohn Marino }
63886d7f5d3SJohn Marino else
63986d7f5d3SJohn Marino {
64086d7f5d3SJohn Marino printf ("localtime (%ld) yields 0\n", (long int) tl);
64186d7f5d3SJohn Marino status = 1;
64286d7f5d3SJohn Marino }
64386d7f5d3SJohn Marino tl1 = tl + by;
64486d7f5d3SJohn Marino if ((tl1 < tl) != (by < 0))
64586d7f5d3SJohn Marino break;
64686d7f5d3SJohn Marino }
64786d7f5d3SJohn Marino }
64886d7f5d3SJohn Marino else
64986d7f5d3SJohn Marino printf ("Usage:\
65086d7f5d3SJohn Marino \t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
65186d7f5d3SJohn Marino \t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
65286d7f5d3SJohn Marino \t%s FROM BY TO - # Do not test those values (for benchmark).\n",
65386d7f5d3SJohn Marino argv[0], argv[0], argv[0]);
65486d7f5d3SJohn Marino
65586d7f5d3SJohn Marino return status;
65686d7f5d3SJohn Marino }
65786d7f5d3SJohn Marino
65886d7f5d3SJohn Marino #endif /* DEBUG */
65986d7f5d3SJohn Marino
66086d7f5d3SJohn Marino /*
66186d7f5d3SJohn Marino Local Variables:
66286d7f5d3SJohn Marino compile-command: "gcc -DDEBUG -Wall -W -O -g mktime.c -o mktime"
66386d7f5d3SJohn Marino End:
66486d7f5d3SJohn Marino */
665