[Zope3-dev] Daylight savings unittest errors

Guido van Rossum guido@python.org
Mon, 09 Jun 2003 15:08:53 -0400


> > The symptom (a roundtrip error of -3600) is consistent with a mktime()
> > implementation that ignores the tm_isdst flag, and always assumes it
> > should use DST if the indicated time is in fact in DST, even if
> > tm_isdst is 0.  I tried this:
> > 
> >     >>> print gm
> >     (2003, 6, 9, 16, 43, 2, 0, 160, 0)
> >     >>> print 'roundtrip:', (time.mktime(gm) - time.timezone) - now
> >     roundtrip: 0.0
> >     >>> gm1 = gm[:8] + (1,)
> >     >>> print 'roundtrip:', (time.mktime(gm1) - time.timezone) - now
> >     roundtrip: -3600.0
> >     >>> gm2 = gm[:8] + (-1,)
> >     >>> print 'roundtrip:', (time.mktime(gm2) - time.timezone) - now
> >     roundtrip: -3600.0
> >     >>> 
> > 
> > What does it do for you?
> 
> I get -3600.0 for all three cases (0, 1, -1) for the last element in the 
> tuple.
> 
> So I guess you're right, it's assuming DST even if we pass a 0 - although 
> in a sense we're *lying* when we're telling it a date in June on this 
> machine isn't DST, when it knows that it is.
> 
> Both the POSIX and BSD manpages for mktime are a bit weird about this 
> detail, basically:
> 
>       A ... zero value for tm_isdst causes mktime() to presume initially
>       that summer time (for example, Daylight Saving Time)... is not in
>       effect for the specified time,
> 
> "presume initially"?  Sounds like another way of saying "hint", where an 
> implementation doesn't necessarily have to pay attention to it if it knows 
> better.

I wonder under what conditions it will decide to ignore the hint...

> > Maybe.  But if your mktime() is broken in this fashion, it will break
> > other Python code too.  I think you should try to get it fixed.
> 
> I agree if it's wrong it should be fixed ...but... even if someone was to 
> convince the BSD people that their mktime() was broke, and future versions 
> were fixed - that doesn't help existing boxes that may try to run Zope3, 
> and find weird stuff happening like cookies expiring an hour before they're 
> supposed to.
> 
> My take is that: Python code that's feeding GMT/UTC into time.mktime() 
> [against the advice of the Python docs] and then tries to adjust for the 
> local timezone manually, instead of using another function that's meant 
> specifically for that exact job and isn't affected by potential underlying 
> platform problems - is a bit whacked and could also be fixed.

I have another suggestion, which you may be able to verify by reading
C code headers (or perhaps even man pages) on your system.

here's the implementation of mktime():

  static PyObject *
  time_mktime(PyObject *self, PyObject *args)
  {
	PyObject *tup;
	struct tm buf;
	time_t tt;
	if (!PyArg_ParseTuple(args, "O:mktime", &tup))
		return NULL;
	tt = time(&tt);
	buf = *localtime(&tt);
	if (!gettmarg(tup, &buf))
		return NULL;
	tt = mktime(&buf);
	if (tt == (time_t)(-1)) {
		PyErr_SetString(PyExc_OverflowError,
				"mktime argument out of range");
		return NULL;
	}
	return PyFloat_FromDouble((double)tt);
  }

What's fishy here is that it is making a call to localtime() with the
current time to initialize "struct tm buf", before calling
gettmarg(tup, buf).

Could it be that on FreeBSD there are timezone info fields in the
struct tm, past the 9 standard fields, which override the tm_isdst
flag?  That would explain why you always get it interpreted as local
time, even when tm_isdst is explicitly set to zero.

If you can confirm this, maybe time_mktime() can be fixed to do
something different if such extra tzinfo fields exist?

--Guido van Rossum (home page: http://www.python.org/~guido/)