This is the mail archive of the
libc-hacker@sourceware.cygnus.com
mailing list for the glibc project.
Re: BUG in mktime (PR libc/783)
- To: Ulrich Drepper <drepper@cygnus.com>
- Subject: Re: BUG in mktime (PR libc/783)
- From: Paul Eggert <eggert@twinsun.com>
- Date: Thu, 8 Oct 1998 00:28:40 -0700 (PDT)
- CC: Andreas Jaeger <jaeger@gnu.org>, Jim Meyering <meyering@ascend.com>, libc-hacker@cygnus.com, Michael Deutschmann <michael@talamasca.wkpowerlink.com>
- References: <87u31fsv75.fsf@ascend.com>
From: Ulrich Drepper <drepper@cygnus.com>
Date: 05 Oct 1998 18:44:26 -0700
The problem is that mktime returns on other systems (e.g, Solaris)
for the illegal broken down date Apr 5 2:00 a non-error
value.... With glibc it results in [an error]. I think the glibc
interpretation is at least equally well.
True, but in practice the glibc mktime interpretation makes date
arithmetic a bit painful. (I can say this, since I wrote mktime. :-)
It turns out that glibc mktime is the only mktime I know of that
rejects times in a spring-forward gap. Other mktimes (Solaris, Olson,
etc.) assume that if you request such a time in within a N-second
spring-forward gap, you get the time T that is N seconds away from the
requested time, such that T's tm_isdst is not equal to the requested
tm_isdst. This is typically more convenient in practice.
Here's a patch to GNU mktime that causes it to behave like other
mktimes in this situation. With this patch, you shouldn't need
to alter GNU `date' to fix this particular example.
1998-10-08 Paul Eggert <eggert@twinsun.com>
* mktime.c (__mktime_internal): When the requested time falls
in a spring-forward gap of size DT, return a time that is DT
away from the requested time, preferring a time whose tm_isdst
differs from the requested value. Bump the max number of
probes from 4 to 6 to account for the extra probes needed to
discover a spring-forward gap in the worst case.
--- mktime.c 1998/09/08 04:59:12 20.3.0.1
+++ mktime.c 1998/10/08 07:19:29 20.3.0.2
@@ -259,12 +259,12 @@
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
@@ -312,13 +312,25 @@
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);