Alexander Belopolsky <alexander.belopol...@gmail.com> writes: > Sorry for a truncated message. Please scroll past the quoted portion. > > On Thu, Apr 9, 2015 at 10:21 PM, Alexander Belopolsky < > alexander.belopol...@gmail.com> wrote: > >> >> On Thu, Apr 9, 2015 at 4:51 PM, Isaac Schwabacher <ischwabac...@wisc.edu> >> wrote: >> >>> > > > Well, you are right, but at least we do have a localtime utility >>> hidden in the email package: >>> > > > >>> > > > >>> from datetime import * >>> > > > >>> from email.utils import localtime >>> > > > >>> print(localtime(datetime.now())) >>> > > > 2015-04-09 15:19:12.840000-04:00 >>> > > > >>> > > > You can read <http://bugs.python.org/issue9527> for the reasons it >>> did not make into datetime. >>> > > >>> > > But that's restricted to the system time zone. Nothing good ever >>> comes from the system time zone... >>> > >>> > Let's solve one problem at a time. ... >>> >>> PEP 431 proposes to import zoneinfo into the stdlib, ... >> >> >> I am changing the subject so that we can focus on one question without >> diverting to PEP-size issues that are better suited for python ideas. >> >> I would like to add a functionality to the datetime module that would >> solve a seemingly simple problem: given a naive datetime instance assumed >> to be in local time, construct the corresponding aware datetime object with >> tzinfo set to an appropriate fixed offset datetime.timezone instance. >> >> Python 3 has this functionality implemented in the email package since >> version 3.3, and it appears to work well even >> in the ambiguous hour >> >> >>> from email.utils import localtime >> >>> from datetime import datetime >> >>> localtime(datetime(2014,11,2,1,30)).strftime('%c %z %Z') >> 'Sun Nov 2 01:30:00 2014 -0400 EDT' >> >>> localtime(datetime(2014,11,2,1,30), isdst=0).strftime('%c %z %Z') >> 'Sun Nov 2 01:30:00 2014 -0500 EST' >> >> However, in a location with a more interesting history, you can get a >> situation that >> > > would look like this in the zoneinfo database: > > $ zdump -v -c 1992 Europe/Kiev > ... > Europe/Kiev Sat Mar 24 22:59:59 1990 UTC = Sun Mar 25 01:59:59 1990 MSK > isdst=0 > Europe/Kiev Sat Mar 24 23:00:00 1990 UTC = Sun Mar 25 03:00:00 1990 MSD > isdst=1 > Europe/Kiev Sat Jun 30 21:59:59 1990 UTC = Sun Jul 1 01:59:59 1990 MSD > isdst=1 > Europe/Kiev Sat Jun 30 22:00:00 1990 UTC = Sun Jul 1 01:00:00 1990 EEST > isdst=1 > Europe/Kiev Sat Sep 28 23:59:59 1991 UTC = Sun Sep 29 02:59:59 1991 EEST > isdst=1 > Europe/Kiev Sun Sep 29 00:00:00 1991 UTC = Sun Sep 29 02:00:00 1991 EET > isdst=0 > ... > > 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: 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]. > The main objection to the isdst flag was that in most situations, > determining whether DST is in effect is as hard as finding the UTC offset, > so reducing the problem of finding the UTC offset to the one of finding the > value for isdst does not solve much. > > I now realize that the problem is simply in the name for the flag. While > we cannot often tell what isdst should be and in some situations the actual > DST status does not differentiate between the two possible times, we can > always say whether we want to get the first or the second time. > > In other words, instead of localtime(dt, isdst=-1), we may want > localtime(dt, which=0) where "which" is used to resolve the ambiguity: > "which=0" means return the first (in UTC order) of the two times and > "which=1" means return the second. (In the non-ambiguous cases "which" is > ignored.) > > An alternative solution would be make localtime(dt) return a list of 0, 1 > or 2 instances, but this will probably make a common usage (the case when > the user does not care which time she gets) more cumbersome. _______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com