-/* mktime: convert a `struct tm' to a time_t value zzzzzz
+/* mktime: convert a `struct tm' to a time_t value
Copyright (C) 1993-1997, 1998 Free Software Foundation, Inc.
Contributed by Paul Eggert (eggert@twinsun.com).
# include <config.h>
#endif
-/* Some systems need this in order to declare localtime_r properly. */
-#ifndef _REENTRANT
-# define _REENTRANT 1
-#endif
-
#ifdef _LIBC
# define HAVE_LIMITS_H 1
-# define HAVE_LOCALTIME_R 1
# define STDC_HEADERS 1
#endif
#ifdef _LIBC
-# define localtime_r __localtime_r
+# define my_mktime_localtime_r __localtime_r
#else
-# if ! HAVE_LOCALTIME_R && ! defined localtime_r
-/* Approximate localtime_r as best we can in its absence. */
-# define localtime_r my_mktime_localtime_r
-static struct tm *localtime_r __P ((const time_t *, struct tm *));
+/* If we're a mktime substitute in a GNU program, then prefer
+ localtime to localtime_r, since many localtime_r implementations
+ are buggy. */
+static struct tm *my_mktime_localtime_r __P ((const time_t *, struct tm *));
static struct tm *
-localtime_r (t, tp)
- const time_t *t;
- struct tm *tp;
+my_mktime_localtime_r (const time_t *t, struct tm *tp)
{
struct tm *l = localtime (t);
if (! l)
*tp = *l;
return tp;
}
-# endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
#endif /* ! _LIBC */
If TP is null, return a nonzero value.
If overflow occurs, yield the low order bits of the correct answer. */
static time_t
-ydhms_tm_diff (year, yday, hour, min, sec, tp)
- int year, yday, hour, min, sec;
- const struct tm *tp;
+ydhms_tm_diff (int year, int yday, int hour, int min, int sec,
+ const struct tm *tp)
{
if (!tp)
return 1;
/* Convert *TP to a time_t value. */
time_t
-mktime (tp)
- struct tm *tp;
+mktime (struct tm *tp)
{
#ifdef _LIBC
/* POSIX.1 8.1.1 requires that whenever mktime() is called, the
__tzset ();
#endif
- return __mktime_internal (tp, localtime_r, &localtime_offset);
+ return __mktime_internal (tp, my_mktime_localtime_r, &localtime_offset);
}
/* Use CONVERT to convert *T to a broken down time in *TP.
If *T is out of range for conversion, adjust it so that
it is the nearest in-range value and then convert that. */
static struct tm *
-ranged_convert (convert, t, tp)
- struct tm *(*convert) __P ((const time_t *, struct tm *));
- time_t *t;
- struct tm *tp;
+ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
+ time_t *t, struct tm *tp)
{
struct tm *r;
compared to what the result would be for UTC without leap seconds.
If *OFFSET's guess is correct, only one CONVERT call is needed. */
time_t
-__mktime_internal (tp, convert, offset)
- struct tm *tp;
- struct tm *(*convert) __P ((const time_t *, struct tm *));
- time_t *offset;
+__mktime_internal (struct tm *tp,
+ struct tm *(*convert) (const time_t *, struct tm *),
+ time_t *offset)
{
- time_t t, dt, t0;
+ time_t t, dt, t0, t1, t2;
struct tm tm;
/* The maximum number of probes (calls to CONVERT) should be enough
to handle any combinations of time zone rule changes, solar time,
- and leap seconds. POSIX.1 prohibits leap seconds, but some hosts
- have them anyway. */
- int remaining_probes = 4;
+ leap seconds, and oscillations around a spring-forward gap.
+ POSIX.1 prohibits leap seconds, but some hosts have them anyway. */
+ int remaining_probes = 6;
/* Time requested. Copy it in case CONVERT modifies *TP; this can
occur if TP is localtime's returned value and CONVERT is localtime. */
tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);
- for (t = t0 + *offset;
+ for (t = t1 = t2 = t0 + *offset;
(dt = ydhms_tm_diff (year, yday, hour, min, sec,
ranged_convert (convert, &t, &tm)));
- t += dt)
- if (--remaining_probes == 0)
+ t1 = t2, t2 = t, t += dt)
+ if (t == t1 && t != t2
+ && (isdst < 0 || tm.tm_isdst < 0
+ || (isdst != 0) != (tm.tm_isdst != 0)))
+ /* We can't possibly find a match, as we are oscillating
+ between two values. The requested time probably falls
+ within a spring-forward gap of size DT. Follow the common
+ practice in this case, which is to return a time that is DT
+ away from the requested time, preferring a time whose
+ tm_isdst differs from the requested value. In practice,
+ this is more useful than returning -1. */
+ break;
+ else if (--remaining_probes == 0)
return -1;
- /* Check whether tm.tm_isdst has the requested value, if any. */
- if (0 <= isdst && 0 <= tm.tm_isdst)
+ /* If we have a match, check whether tm.tm_isdst has the requested
+ value, if any. */
+ if (dt == 0 && 0 <= isdst && 0 <= tm.tm_isdst)
{
int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
if (dst_diff)
\f
/*
Local Variables:
-compile-command: "gcc -DDEBUG -D__EXTENSIONS__ -DHAVE_LIMITS_H -DHAVE_LOCALTIME_R -DSTDC_HEADERS -Wall -W -O -g mktime.c -o mktime"
+compile-command: "gcc -DDEBUG -DHAVE_LIMITS_H -DSTDC_HEADERS -Wall -W -O -g mktime.c -o mktime"
End:
*/