[Datetime-SIG] Another round on error-checking

Tim Peters tim.peters at gmail.com
Wed Sep 2 21:59:03 CEST 2015

>>> There are two more cases:
>>> (1) datetime.now() will return fold=1 instances during one hour each
>>> year;
>>> (2) datetime.fromtimestamp(s) will return fold=1 instances for some
>>> values of s.

>> Sure - but anything reflecting how a local clock actually behaves is
>> outside of "naive time".  Clocks in naive time never jump forward or
>> backward.  Specifically, .now() and .fromtimestamp() are also
>> operations outside of naive time.

> I agree, but the worst thing we can do to our users is to plant a time bomb
> that will go off once a year.  Suppose someone has a program that uses naive
> local times and relies on t < prev_t test to detect the fall-back fold and
> do something about it.  If we don't ignore fold in naive datetime
> comparisons - this program will start producing incorrect results.

Yes, but I believe it's worse:  that it's impossible for PEP 495 to be
wholly backward compatible regardless of whether intrazone comparison
ignores `fold`.  It's not just "stare at one line of code" that counts
for compatibility, breaking former invariants also counts.  Like
Stewart mentioned just before, anyone in their right mind ;-)
_implicitly_ assumed all along that

    x == y


    x.utctimetuple() == y.utctimetuple()

and, indeed,

    x.astimezone(SOMETZINFO) == y.astimezone(SOMETZINFO)

too for any value of SOMETZINFO.

PEP 495's original form breaks those (among others) - it's not
credible to claim that no existing code could possibly be relying on
those (or relying on total datetime ordering, etc).  That may not be
reflected in any single line of code, but only in what code _didn't_
do to worm around "a problem" it reasonably - perhaps not even
consciously - assumed could never happen.

The only way I see to be wholly backward compatible is to default to
fold = -1, where fold < 0 is wholly ignored by everything, always.
That's the only way to be sure no code breaks, because no behaviors
whatsoever change, in any context, except possibly for the
datetime.__repr_() string produced.  Not just in single lines of code,
but no invariants break either.

But that also means .now() and .fromtimestamp() and .fromutc() must
set set fold = -1, lest a fold=1 sneak in (your "time bomb once a
year" scenario).  Then we either need different fold-aware versions of
all such functions, or new optional foldaware=False arguments on all
such functions.  But then it's so annoying and error-prone to use, who
would bother?  Whoever responds with "global flag" will be shot ;-)

> Fortunately, we don't need to do anything about naive times.  The hash
> invariant is only violated by aware instances.

Proving yet again that naive time is the only way to go ;-)

> I think what you are really fighting against is the notion that for regular
> times, fold=1 is just an alternative spelling for fold=0 times.  It looks
> like you would rather see fold=1 as some different (and invalid) time.

In naive time, `fold=1` is simply senseless.  It "should be" ignored
in naive time.  But there is no wall between "naive time" and
"timeline time" in datetime's design - indeed, there is no _explicit_
way to say which you have in mind.  Something has to give, because an
aware datetime can be _viewed_ as being either in naive time or as in
timeline time.  That's in the programmer's head.  Since fold=1 makes
no sense in naive time, the sanest thing is to take it as meaning the
datetime can _only_ be viewed as being in timeline time.  We already
know that solves a world of problems.  But it will create others.
Alas, best I can see, nothing short of fold < 0 can create _no_
problems (except for making it all kinds of pain to get fold-aware
behaviors instead).

> Think of the German A and B hours: are regular hours A or B?  The German
> standard say that they are neither, but PEP 495 say that they are both:
> 2A is the same as 2B  unless "2" in the fold and that allows you not to display
> A/B in those cases.

I'm not sure appealing to German A and B hours really clarifies it ;-)

> Folds do not exist in naive time, so all times are regular and therefore
> time(h, m, s, us,  fold=0) == time(h, m, s, us,  fold=1) always.

As above, we can have no real idea whether the programmer _intends_
that an aware datetime lives in naive time or timeline time.  fold=1
screams "timeline".

More information about the Datetime-SIG mailing list