[Datetime-SIG] PEP-495 - Strict Invalid Time Checking

Alexander Belopolsky alexander.belopolsky at gmail.com
Tue Aug 25 16:51:16 CEST 2015


On Tue, Aug 25, 2015 at 9:30 AM, Stuart Bishop <stuart at stuartbishop.net>
wrote:

> As mentioned elsewhere, pytz requires strict checking to remain
> backwards compatible.
>

Can you provide the specific examples where strict checking is required?
Since pytz already has a disambiguation solution, PEP 495 is not as
indispensable for it as it is for datetime or dateutil.  However, there is
one case I can think of where pytz will benefit: with the PEP, it will be
possible to make say Eastern.localize(datetime.now()) work correctly at all
times.  If for backward compatibility, you want to continue raising
AmbiguousTimeError during one hour each year, I am sure you will figure out
how to make Eastern.localize(datetime.now(), isdst=None).  (Hint: Don't
change anything in this branch of your code.)



> The description above does not match the desired
> behaviour though.
>

I assume you refer to "Another suggestion was to use first=-1 or first=None
to indicate that
the program truly has no means to deal with the folds and gaps and
dt.utcoffset() should raise an error whenever dt represents an
ambiguous or missing local time."

It looks like you want to make it impossible to construct invalid dt
instances.  In other words, you want to make dt.replace(fold=-1) or
dt.replace(tzinfo=Eastern) raise an error under certain circumstances.  Is
this right?


> pytz users need to optionally have exceptions raised
> when they try to construct an invalid or ambiguous datetime instance,
>

This is a legitimate need, by why does it need to be done in datetime
rather than in pytz itself?  You already ignore the datetime(...,
tzinfo=...) constructor and require your users to call localize() instead.
What stops you from providing a function strict_datetime() that will
perform any checks that you or your users desire?


> directly via __new__ or indirectly with something like  dt.replace().
>

__new__ and .replace() are low level methods called in many performance
critical places.  We cannot afford to call arbitrary python code in those
methods.


> If these methods are called with first=None, it will be passed through
> to dt.utcoffset() and it may raise an exception.
>

This part I don't understand.  If __new__ raises an exception - you will
have no instance to "be passed through to dt.utcoffset()."


> dt.utcoffset() will
> only ever raise an exception when the user has explicitly requested
> construction of a datetime with strict checking.
>

If you allow constructing instances with failing .utcoffset(), these
instances will make innocent-looking code capable of raising an error.  For
example dt1 in {dt2} will raise the same error as dt2.utcoffset()
 regardless of what dt1 is.  You will have similar problems with dt1 ==
dt2, dictionary lookups, list searches and so on.


> It will never raise
> an exception in normal operation, including arithmetic, and could not
> cause problems in the situations you cite.
>

If I have two instances dt1 and dt2 with different .tzinfo and
dt1.utcoffset() raises an exception.  What dt1 - dt2 should return?  or dt1
== dt2?  or hash(dt1)?

>
> This would require the first argument to be available in all the
> methods that can construct datetime instances, including things not
> mentioned in the PEP like dt.astimezone()
>

.astimezone() is mentioned in the PEP [1], but I should probably add
explicit discussion of how it should handle invalid times.  In my view, the
behavior of .astimezone() follows straight from that of .utcoffset(), but
it may not be obvious from the PEP alone.


> (Where I think it might be
> using the first flag from the original dt instance, rather than
> allowing the user to specify which side of the fold in the target
> zone).
>

 .astimezone() does not and need not "allow the user to specify which side
of the fold in the target zone."  As long as it knows how to interpret the
time that it is given (disambiguate the fold and "normalize" the gap) it
should be able to set the fold=1 attribute correctly in the result if it
happen to fall into the repeated hour and should never produce a time that
is in the gap of the target zone.

[1]:
https://www.python.org/dev/peps/pep-0495/#conversion-from-naive-to-aware
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/datetime-sig/attachments/20150825/4ea4f61f/attachment-0001.html>


More information about the Datetime-SIG mailing list