[Python-Dev] Aware datetime from naive local time Was: Status on PEP-431 Timezones
Akira Li
4kir4.1i at gmail.com
Sat Apr 18 02:19:41 CEST 2015
On Thu, Apr 16, 2015 at 1:14 AM, Alexander Belopolsky <
alexander.belopolsky at gmail.com> wrote:
>
> On Wed, Apr 15, 2015 at 4:46 PM, Akira Li <4kir4.1i at gmail.com> wrote:
>
>> > Look what happened on July 1, 1990. At 2 AM, the clocks in Ukraine were
>> > moved back one hour. So times like 01:30 AM happened twice there on
>> that
>> > day. Let's see how Python handles this situation
>> >
>> > $ TZ=Europe/Kiev python3
>> >>>> from email.utils import localtime
>> >>>> from datetime import datetime
>> >>>> localtime(datetime(1990,7,1,1,30)).strftime('%c %z %Z')
>> > 'Sun Jul 1 01:30:00 1990 +0400 MSD'
>> >
>> > So far so good, I've got the first of the two 01:30AM's. But what if I
>> > want the other 01:30AM? Well,
>> >
>> >>>> localtime(datetime(1990,7,1,1,30), isdst=0).strftime('%c %z %Z')
>> > 'Sun Jul 1 01:30:00 1990 +0300 EEST'
>> >
>> > gives me "the other 01:30AM", but it is counter-intuitive: I have to ask
>> > for the standard (winter) time to get the daylight savings (summer)
>> time.
>> >
>>
>> It looks incorrect. Here's the corresponding pytz code:
>>
>> from datetime import datetime
>> import pytz
>>
>> tz = pytz.timezone('Europe/Kiev')
>> print(tz.localize(datetime(1990, 7, 1, 1, 30),
>> is_dst=False).strftime('%c %z %Z'))
>> # -> Sun Jul 1 01:30:00 1990 +0300 EEST
>> print(tz.localize(datetime(1990, 7, 1, 1, 30),
>> is_dst=True).strftime('%c %z %Z'))
>> # -> Sun Jul 1 01:30:00 1990 +0400 MSD
>>
>> See also "Enhance support for end-of-DST-like ambiguous time" [1]
>>
>> [1] https://bugs.launchpad.net/pytz/+bug/1378150
>>
>> `email.utils.localtime()` is broken:
>>
>
> If you think there is a bug in email.utils.localtime - please open an
> issue at <bugs.python.org>.
>
>
Your question below suggests that you believe it is not a bug i.e.,
`email.utils.localtime()` is broken *by design* unless you think it is ok
to ignore `+0400 MSD`.
pytz works for me (I can get both `+0300 EEST` and `+0400 MSD`). I don't
think `localtime()` can be fixed without the tz database. I don't know
whether it should be fixed, let somebody else who can't use pytz to pioneer
the issue. The purpose of the code example is to **inform** that
`email.utils.localtime()` fails (it returns only +0300 EEST) in this case:
>> from datetime import datetime
>> from email.utils import localtime
>>
>> print(localtime(datetime(1990, 7, 1, 1, 30)).strftime('%c %z %Z'))
>> # -> Sun Jul 1 01:30:00 1990 +0300 EEST
>> print(localtime(datetime(1990, 7, 1, 1, 30), isdst=0).strftime('%c %z
>> %Z'))
>> # -> Sun Jul 1 01:30:00 1990 +0300 EEST
>> print(localtime(datetime(1990, 7, 1, 1, 30), isdst=1).strftime('%c %z
>> %Z'))
>> # -> Sun Jul 1 01:30:00 1990 +0300 EEST
>> print(localtime(datetime(1990, 7, 1, 1, 30), isdst=-1).strftime('%c %z
>> %Z'))
>> # -> Sun Jul 1 01:30:00 1990 +0300 EEST
>>
>>
>> Versions:
>>
>> $ ./python -V
>> Python 3.5.0a3+
>> $ dpkg -s tzdata | grep -i version
>> Version: 2015b-0ubuntu0.14.04
>>
>> > The uncertainty about how to deal with the repeated hour was the reason
>> why
>> > email.utils.localtime-like interface did not make it to the datetime
>> > module.
>>
>> "repeated hour" (time jumps back) can be treated like a end-of-DST
>> transition, to resolve ambiguities [1].
>
>
> I don't understand what you are complaining about. It is quite possible
> that pytz uses is_dst flag differently from the way email.utils.localtime
> uses isdst.
>
> I was not able to find a good description of what is_dst means in pytz,
> but localtime's isdst is documented as follows:
>
> a positive or zero value for *isdst* causes localtime to
> presume initially that summer time (for example, Daylight Saving Time)
> is or is not (respectively) in effect for the specified time.
>
> Can you demonstrate that email.utils.localtime does not behave as
> documented?
>
No need to be so defensive about it. *""repeated hour" (time jumps back)
can be treated like a end-of-DST transition, to resolve ambiguities [1]."*
is just a *an example* on how to fix the problem in the same way how it is
done in pytz:
>>> from datetime import datetime
>>> import pytz
>>> tz = pytz.timezone('Europe/Kiev')
>>> after = tz.localize(datetime(1990, 7, 1, 1, 30), is_dst=False)
>>> before = tz.localize(datetime(1990, 7, 1, 1, 30), is_dst=True)
>>> before < after
True
>>> before
datetime.datetime(1990, 7, 1, 1, 30, tzinfo=<DstTzInfo 'Europe/Kiev'
MSD+4:00:00 DST>)
>>> after
datetime.datetime(1990, 7, 1, 1, 30, tzinfo=<DstTzInfo 'Europe/Kiev'
EEST+3:00:00 DST>)
>>> before.astimezone(pytz.utc)
datetime.datetime(1990, 6, 30, 21, 30, tzinfo=<UTC>)
>>> after.astimezone(pytz.utc)
datetime.datetime(1990, 6, 30, 22, 30, tzinfo=<UTC>)
>>> before.dst()
datetime.timedelta(0, 3600)
>>> after.dst()
datetime.timedelta(0, 3600)
>>> pytz.OLSON_VERSION
'2015b'
Here's "summer time" in both cases i.e., it is not *true* end-of-DST
transition (that is why I've used the word *"like"* above).
If we ignore ambiguous time that may occur more than twice then a boolean
flag such as pytz's is_dst is *always* enough to resolve the ambiguity
assuming we have access to the tz database.
And yes, the example demonstrates that the behavior of pytz's is_dst and
localtime()'s isdst is different. The example just shows that the current
behavior of localtime() doesn't allow to get `+0400 DST` (on my system, see
the software versions above) and how to get it (*adopt* the pytz behavior
-- you need zoneinfo for that) i.e., the message is a problem and a
possible solution -- no complains.
[1] https://bugs.launchpad.net/pytz/+bug/1378150
--
Akira.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20150418/cc3ac706/attachment.html>
More information about the Python-Dev
mailing list