Bug 15520 - mktime() fails under certain conditions
Summary: mktime() fails under certain conditions
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: Unspecified
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-bugs (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 1999-12-16 20:20 UTC by Andre Albsmeier
Modified: 2000-08-14 21:52 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Andre Albsmeier 1999-12-16 20:20:01 UTC
mktime() fails if it is called with a time that is exactly
the moment when daylight saving time is starting.

The configure program of certain gnu software tests mktime()
for the failure described above. This fails on FreeBSD and
so configure assumes there is no working mktime available.
This behaviour was observed when configure'ing gnu tar-1.1.3.

Fix: 

Unknown, unfortunatley...
How-To-Repeat: 
The following program is the testing code of gnu's configure
plus three printf's to show the returned value and if the
problem exists. I have tried it also on HP-UX 10.20 and on
IRIX 5.3 where it works. In this cases the result returned
by mktime() is 891766800.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void main ()
{   
  time_t t;
  struct tm tm;
    
  /* Use the portable POSIX.1 specification "TZ=PST8PDT,M4.1.0,M10.5.0"
     instead of "TZ=America/Vancouver" in order to detect the bug even
     on systems that don't support the Olson extension, or don't have the
     full zoneinfo tables installed.  */

  putenv ("TZ=PST8PDT,M4.1.0,M10.5.0");
    
  tm.tm_year  = 98;
  tm.tm_mon   =  3;
  tm.tm_mday  =  5;
  tm.tm_hour  =  2;
  tm.tm_min   =  0;
  tm.tm_sec   =  0;
  tm.tm_isdst = -1;
  t=mktime(&tm);
  printf("%lu, ", t);
  if (t== (time_t)-1) {
    printf("Error\n");
    exit (1);
  }
  printf("OK\n");
}


For better understanding, here is the complete conftest.c that is used
by configure:

#line 4392 "configure"
#include "confdefs.h"
/* Test program from Paul Eggert (eggert@twinsun.com)
   and Tony Leneis (tony@plaza.ds.adp.com).  */
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
 #else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif

#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#if !HAVE_ALARM
# define alarm(X) /* empty */
#endif

/* Work around redefinition to rpl_putenv by other config tests.  */
#undef putenv

static time_t time_t_max;

/* Values we'll use to set the TZ environment variable.  */
static const char *const tz_strings[] = {
  (const char *) 0, "TZ=GMT0", "TZ=JST-9",
  "TZ=EST+3EDT+2,M10.1.0/00:00:00,M2.3.0/00:00:00"
};
#define N_STRINGS (sizeof (tz_strings) / sizeof (tz_strings[0]))

/* Fail if mktime fails to convert a date in the spring-forward gap.
   Based on a problem report from Andreas Jaeger.  */
static void
spring_forward_gap ()
{
  /* glibc (up to about 1998-10-07) failed this test) */
  struct tm tm;

  /* Use the portable POSIX.1 specification "TZ=PST8PDT,M4.1.0,M10.5.0"
     instead of "TZ=America/Vancouver" in order to detect the bug even
     on systems that don't support the Olson extension, or don't have the
     full zoneinfo tables installed.  */
  putenv ("TZ=PST8PDT,M4.1.0,M10.5.0");

  tm.tm_year = 98;
  tm.tm_mon = 3;
  tm.tm_mday = 5;
  tm.tm_hour = 2;
  tm.tm_min = 0;
  tm.tm_sec = 0;
  tm.tm_isdst = -1;
  if (mktime (&tm) == (time_t)-1)
    exit (1);
}

static void
mktime_test (now)
     time_t now;
{
  struct tm *lt;
  if ((lt = localtime (&now)) && mktime (lt) != now)
    exit (1);
  now = time_t_max - now;
  if ((lt = localtime (&now)) && mktime (lt) != now)
    exit (1);
}

static void
irix_6_4_bug ()
{
  /* Based on code from Ariel Faigon.  */
  struct tm tm;
  tm.tm_year = 96;
  tm.tm_mon = 3;
  tm.tm_mday = 0;
  tm.tm_hour = 0;
  tm.tm_min = 0;
  tm.tm_sec = 0;
  tm.tm_isdst = -1;
  mktime (&tm);
  if (tm.tm_mon != 2 || tm.tm_mday != 31)
    exit (1);
}

static void
bigtime_test (j)
     int j;
{
  struct tm tm;
  time_t now;
  tm.tm_year = tm.tm_mon = tm.tm_mday = tm.tm_hour = tm.tm_min = tm.tm_sec = j;
  now = mktime (&tm);
  if (now != (time_t) -1)
    {
      struct tm *lt = localtime (&now);
      if (! (lt
	     && lt->tm_year == tm.tm_year
	     && lt->tm_mon == tm.tm_mon
	     && lt->tm_mday == tm.tm_mday
	     && lt->tm_hour == tm.tm_hour
	     && lt->tm_min == tm.tm_min
	     && lt->tm_sec == tm.tm_sec
	     && lt->tm_yday == tm.tm_yday
	     && lt->tm_wday == tm.tm_wday
	     && ((lt->tm_isdst < 0 ? -1 : 0 < lt->tm_isdst)
		  == (tm.tm_isdst < 0 ? -1 : 0 < tm.tm_isdst))))
	exit (1);
    }
}

int
main ()
{
  time_t t, delta;
  int i, j;

  /* This test makes some buggy mktime implementations loop.
     Give up after 60 seconds; a mktime slower than that
     isn't worth using anyway.  */
  alarm (60);

  for (time_t_max = 1; 0 < time_t_max; time_t_max *= 2)
    continue;
  time_t_max--;
  delta = time_t_max / 997; /* a suitable prime number */
  for (i = 0; i < N_STRINGS; i++)
    {
      if (tz_strings[i])
	putenv (tz_strings[i]);

      for (t = 0; t <= time_t_max - delta; t += delta)
	mktime_test (t);
      mktime_test ((time_t) 60 * 60);
      mktime_test ((time_t) 60 * 60 * 24);

      for (j = 1; 0 < j; j *= 2)
        bigtime_test (j);
      bigtime_test (j - 1);
    }
  irix_6_4_bug ();
  spring_forward_gap ();
  exit (0);
}
Comment 1 Poul-Henning Kamp 1999-12-16 20:24:41 UTC
In message <199912162016.VAA97593@internal>, Andre Albsmeier writes:

>mktime() fails if it is called with a time that is exactly
>the moment when daylight saving time is starting.

Well, at least for the "spring forward case" that time doesn't
exist:

	01:59:57
	01:59:58
	01:59:59
	03:00:00

There is no 02:00:00 that night.  If they test for that, they're
crazy.

--
Poul-Henning Kamp             FreeBSD coreteam member
phk@FreeBSD.ORG               "Real hackers run -current on their laptop."
FreeBSD -- It will take a long time before progress goes too far!
Comment 2 Andre Albsmeier 1999-12-16 20:36:47 UTC
On Thu, 16-Dec-1999 at 21:24:41 +0100, Poul-Henning Kamp wrote:
> In message <199912162016.VAA97593@internal>, Andre Albsmeier writes:
> 
> >mktime() fails if it is called with a time that is exactly
> >the moment when daylight saving time is starting.
> 
> Well, at least for the "spring forward case" that time doesn't
> exist:
> 
> 	01:59:57
> 	01:59:58
> 	01:59:59
> 	03:00:00
> 
> There is no 02:00:00 that night.  If they test for that, they're
> crazy.

Yes, the time does not exist. However, I only wonder it our
behaviour of returning an error is correct in this case. I don't
trust the GNU/Linux guys as much as I trust FreeBSD :-) but there
might be a reason they are testing it.

I was already asked in private email if the mktime() should
succeed according to POSIX.1... Does somebody know anything
about that?

	-Andre
Comment 3 Garrett A. Wollman 1999-12-16 20:39:19 UTC
<<On Thu, 16 Dec 1999 21:16:20 +0100 (CET), Andre Albsmeier <andre.albsmeier@mchp.siemens.de> said:

> mktime() fails if it is called with a time that is exactly
> the moment when daylight saving time is starting.

> The configure program of certain gnu software tests mktime()
> for the failure described above. This fails on FreeBSD and
> so configure assumes there is no working mktime available.

``certain gnu software'' is broken.  Bring it up with the maintainers.

-GAWollman

--
Garrett A. Wollman   | O Siem / We are all family / O Siem / We're all the same
wollman@lcs.mit.edu  | O Siem / The fires of freedom 
Opinions not those of| Dance in the burning flame
MIT, LCS, CRS, or NSA|                     - Susan Aglukark and Chad Irschick
Comment 4 Poul-Henning Kamp 1999-12-16 20:52:17 UTC
In message <19991216213647.A34480@internal>, Andre Albsmeier writes:
>On Thu, 16-Dec-1999 at 21:24:41 +0100, Poul-Henning Kamp wrote:
>> In message <199912162016.VAA97593@internal>, Andre Albsmeier writes:
>> 
>> There is no 02:00:00 that night.  If they test for that, they're
>> crazy.
>
>Yes, the time does not exist. However, I only wonder it our
>behaviour of returning an error is correct in this case. I don't
>trust the GNU/Linux guys as much as I trust FreeBSD :-) but there
>might be a reason they are testing it.
>
>I was already asked in private email if the mktime() should
>succeed according to POSIX.1... Does somebody know anything
>about that?

Well, Hum.  It seems to say that the fields are not constrained
to their normal domains:

	The original values of the tm_wday and tm_yday components
	of the structure are ignored, and the original values of
	the other components are not restricted to the ranges
	described in the <time.h> entry.

It does not describe what should happen if I ask it to make a time
out of 25:100:100, but I guess most of us can agree what it should
do.

The trouble with passing it 02:00:00 or for that matter 02:59:00
on the "spring forward" night, is that the time doesn't exist in
the first place:  Ie,  does 02:10:00 represent

	01:59:59 + 10:59 = 03:10:00
or 
	03:00:00 - 50:00 = 01:10:00

Since no sane argument either way really holds water, I think
returning an error is correct.

--
Poul-Henning Kamp             FreeBSD coreteam member
phk@FreeBSD.ORG               "Real hackers run -current on their laptop."
FreeBSD -- It will take a long time before progress goes too far!
Comment 5 Andre Albsmeier 1999-12-16 21:14:11 UTC
On Thu, 16-Dec-1999 at 21:52:17 +0100, Poul-Henning Kamp wrote:
> In message <19991216213647.A34480@internal>, Andre Albsmeier writes:
> >On Thu, 16-Dec-1999 at 21:24:41 +0100, Poul-Henning Kamp wrote:
> >> In message <199912162016.VAA97593@internal>, Andre Albsmeier writes:
> >> 
> >> There is no 02:00:00 that night.  If they test for that, they're
> >> crazy.
> >
> >Yes, the time does not exist. However, I only wonder it our
> >behaviour of returning an error is correct in this case. I don't
> >trust the GNU/Linux guys as much as I trust FreeBSD :-) but there
> >might be a reason they are testing it.
> >
> >I was already asked in private email if the mktime() should
> >succeed according to POSIX.1... Does somebody know anything
> >about that?
> 
> Well, Hum.  It seems to say that the fields are not constrained
> to their normal domains:
> 
> 	The original values of the tm_wday and tm_yday components
> 	of the structure are ignored, and the original values of
> 	the other components are not restricted to the ranges
> 	described in the <time.h> entry.
> 
> It does not describe what should happen if I ask it to make a time
> out of 25:100:100, but I guess most of us can agree what it should
> do.
> 
> The trouble with passing it 02:00:00 or for that matter 02:59:00
> on the "spring forward" night, is that the time doesn't exist in
> the first place:  Ie,  does 02:10:00 represent
> 
> 	01:59:59 + 10:59 = 03:10:00
> or 
> 	03:00:00 - 50:00 = 01:10:00
> 
> Since no sane argument either way really holds water, I think
> returning an error is correct.

I just tried to find out how HP-UX 10.20 and IRIX 5.3 interpret this;
they both behave in the same way:

When setting tm_hour to 2 (the illegal value) the result is 891766800.
When using 1 for tm_hour we also get 891766800.
When using 0 for tm_hour we get 891763200, this is -3600 from above.
When using 3 for tm_hour the result is 891770400, this is +3600 compared
to the first two.

These two interpret the setting of tm_hour to 2 as 1. (That doesn't
imply that I think this is correct).

	-Andre
Comment 6 Andre Albsmeier 1999-12-17 07:57:38 UTC
On Thu, 16-Dec-1999 at 21:52:17 +0100, Poul-Henning Kamp wrote:
> In message <19991216213647.A34480@internal>, Andre Albsmeier writes:
> >On Thu, 16-Dec-1999 at 21:24:41 +0100, Poul-Henning Kamp wrote:
> >> In message <199912162016.VAA97593@internal>, Andre Albsmeier writes:
> >> 
> >> There is no 02:00:00 that night.  If they test for that, they're
> >> crazy.
> >
> >Yes, the time does not exist. However, I only wonder it our
> >behaviour of returning an error is correct in this case. I don't
> >trust the GNU/Linux guys as much as I trust FreeBSD :-) but there
> >might be a reason they are testing it.
> >
> >I was already asked in private email if the mktime() should
> >succeed according to POSIX.1... Does somebody know anything
> >about that?
> 
> Well, Hum.  It seems to say that the fields are not constrained
> to their normal domains:
> 
> 	The original values of the tm_wday and tm_yday components
> 	of the structure are ignored, and the original values of
> 	the other components are not restricted to the ranges
> 	described in the <time.h> entry.
> 
> It does not describe what should happen if I ask it to make a time
> out of 25:100:100, but I guess most of us can agree what it should
> do.
> 
> The trouble with passing it 02:00:00 or for that matter 02:59:00
> on the "spring forward" night, is that the time doesn't exist in
> the first place:  Ie,  does 02:10:00 represent
> 
> 	01:59:59 + 10:59 = 03:10:00
> or 
> 	03:00:00 - 50:00 = 01:10:00
> 
> Since no sane argument either way really holds water, I think
> returning an error is correct.

As I wrote initally, the problem arises when configure tries to
determine if mktime() works. I have now looked what happens in this
case: The program replaces mktime with its own call. For the record,
here is the interesting part (mktime.c, around line 277):

    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;


	-Andre
Comment 7 Brian Somers freebsd_committer freebsd_triage 2000-08-14 21:47:04 UTC
State Changed
From-To: open->closed

I (and many others) believe that it is more correct to return an error 
from mktime() when it's asked to construct a time from some time that 
can't happen. 

Others belive that mktime() should succeed during leap-periods. 

POSIX (I'm told) doesn't specify either. 

IMHO this is therefore a non-issue.  FreeBSD's mktime() returns 
an error and will stay that way.