[Datetime-SIG] Another round on error-checking

Tim Peters tim.peters at gmail.com
Mon Aug 31 20:53:59 CEST 2015


[Tim]
>> PEP 495 specifies resolving such cases by magic, in essentially
>> arbitrary (from the user's point of view) ways.  This isn't for
>> backward compatibility, because 495-compliant tzinfos don't currently
>> exist

[Alex]
> .. but tzinfo.fromutc() [1] does exist

I know - I wrote it ;-)

> and the first thing happening in that code is an unguarded call
> to utcoffset() on a UTC datetime with a transplanted tzinfo. [2]  A
> trick like this have been valid in datetime since its introduction
> and has been used in numerous places since.

But "continuing to never raise an exception" does not imply
"continuing to work as intended".  All such code needs to be carefully
audited in a PEP-495 world to ensure that the problem-case return
values work as intended with the old code and the new PEP 495 behavior
_if_ a 495-compliant tzinfo object is used.

> You know that with a little effort universal .fromutc() can be rewritten to be
> PEP 495 compliant.
>
> If you think allowing datetime.utcoffset() to raise an exception is an
> option, let's first see how one can rewrite universal tzinfo.fromutc() for
> that design.  Even though I don't think reusing a universal .fromutc() is a
> good option for tzinfo implementers, it gives an example of simple but
> non-trivial datetime manipulation code which PEP 495 is designed not to
> break.

Eh,.  _If_ .utcoffet() could raise exceptions, then the obvious way
would be to catch the exceptions and, in each exceptional case,
implement whichever behavior is most appropriate in context.  That
could be used to implement any desired behavior whatsoever.

> [1]: https://hg.python.org/cpython/file/v3.5.0rc2/Lib/datetime.py#l957
> [2]: https://hg.python.org/cpython/file/v3.5.0rc2/Lib/datetime.py#l965


BTW, it just occurred to me that PEP 495 has already broken
datetime.__hash__.  That is, the fundamental invariant a hash
implementation must satisfy is:

    A == B implies hash(A) == hash(B)

But, under 495, equality of datetimes ignores `fold`, but .utcoffset()
does not, and

        def __hash__(self):
            tzoff = self.utcoffset()
            if tzoff is None:
                ...
            else:
                ...
                self._hashcode = hash(timedelta(days, seconds,
self.microsecond) - tzoff)

So two datetimes in a fold, one with fold=0 and the other with fold=1,
will compare equal but almost certainly have different hashes.  That's
a real puzzle.  But the kind of error-checking I'm discussing doesn't
make that _worse_ ;-)

WRT the current __hash__ problem, perhaps it's enough for the PEP to
note that ... in some number of cases including hash(), `fold` does a
make difference ;-)


More information about the Datetime-SIG mailing list