From 909a6f626ff343937cd3f06fda996870e7890724 Mon Sep 17 00:00:00 2001 From: Marco Buttu Date: Sat, 18 Mar 2017 17:59:33 +0100 Subject: bpo-27200: Fix doctests in programming.rst and datetime.rst (#401) --- Doc/conf.py | 1 + Doc/faq/programming.rst | 4 +- Doc/includes/tzinfo-examples.py | 175 ---------------------------------------- Doc/includes/tzinfo_examples.py | 175 ++++++++++++++++++++++++++++++++++++++++ Doc/library/datetime.rst | 10 ++- 5 files changed, 185 insertions(+), 180 deletions(-) delete mode 100644 Doc/includes/tzinfo-examples.py create mode 100644 Doc/includes/tzinfo_examples.py diff --git a/Doc/conf.py b/Doc/conf.py index 18aebb6..c1c2472 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -8,6 +8,7 @@ import sys, os, time sys.path.append(os.path.abspath('tools/extensions')) +sys.path.append(os.path.abspath('includes')) # General configuration # --------------------- diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 9c5e20d..d9bdb21 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -1849,7 +1849,7 @@ containing statements like :: will continue to work with the old version of the imported objects. If the module contains class definitions, existing class instances will *not* be updated to use the new class definition. This can result in the following -paradoxical behaviour: +paradoxical behaviour:: >>> import importlib >>> import cls @@ -1860,7 +1860,7 @@ paradoxical behaviour: False The nature of the problem is made clear if you print out the "identity" of the -class objects: +class objects:: >>> hex(id(c.__class__)) '0x7352a0' diff --git a/Doc/includes/tzinfo-examples.py b/Doc/includes/tzinfo-examples.py deleted file mode 100644 index ae5a509..0000000 --- a/Doc/includes/tzinfo-examples.py +++ /dev/null @@ -1,175 +0,0 @@ -from datetime import tzinfo, timedelta, datetime, timezone - -ZERO = timedelta(0) -HOUR = timedelta(hours=1) -SECOND = timedelta(seconds=1) - -# A class capturing the platform's idea of local time. -# (May result in wrong values on historical times in -# timezones where UTC offset and/or the DST rules had -# changed in the past.) -import time as _time - -STDOFFSET = timedelta(seconds = -_time.timezone) -if _time.daylight: - DSTOFFSET = timedelta(seconds = -_time.altzone) -else: - DSTOFFSET = STDOFFSET - -DSTDIFF = DSTOFFSET - STDOFFSET - -class LocalTimezone(tzinfo): - - def fromutc(self, dt): - assert dt.tzinfo is self - stamp = (dt - datetime(1970, 1, 1, tzinfo=self)) // SECOND - args = _time.localtime(stamp)[:6] - dst_diff = DSTDIFF // SECOND - # Detect fold - fold = (args == _time.localtime(stamp - dst_diff)) - return datetime(*args, microsecond=dt.microsecond, - tzinfo=self, fold=fold) - - def utcoffset(self, dt): - if self._isdst(dt): - return DSTOFFSET - else: - return STDOFFSET - - def dst(self, dt): - if self._isdst(dt): - return DSTDIFF - else: - return ZERO - - def tzname(self, dt): - return _time.tzname[self._isdst(dt)] - - def _isdst(self, dt): - tt = (dt.year, dt.month, dt.day, - dt.hour, dt.minute, dt.second, - dt.weekday(), 0, 0) - stamp = _time.mktime(tt) - tt = _time.localtime(stamp) - return tt.tm_isdst > 0 - -Local = LocalTimezone() - - -# A complete implementation of current DST rules for major US time zones. - -def first_sunday_on_or_after(dt): - days_to_go = 6 - dt.weekday() - if days_to_go: - dt += timedelta(days_to_go) - return dt - - -# US DST Rules -# -# This is a simplified (i.e., wrong for a few cases) set of rules for US -# DST start and end times. For a complete and up-to-date set of DST rules -# and timezone definitions, visit the Olson Database (or try pytz): -# http://www.twinsun.com/tz/tz-link.htm -# http://sourceforge.net/projects/pytz/ (might not be up-to-date) -# -# In the US, since 2007, DST starts at 2am (standard time) on the second -# Sunday in March, which is the first Sunday on or after Mar 8. -DSTSTART_2007 = datetime(1, 3, 8, 2) -# and ends at 2am (DST time) on the first Sunday of Nov. -DSTEND_2007 = datetime(1, 11, 1, 2) -# From 1987 to 2006, DST used to start at 2am (standard time) on the first -# Sunday in April and to end at 2am (DST time) on the last -# Sunday of October, which is the first Sunday on or after Oct 25. -DSTSTART_1987_2006 = datetime(1, 4, 1, 2) -DSTEND_1987_2006 = datetime(1, 10, 25, 2) -# From 1967 to 1986, DST used to start at 2am (standard time) on the last -# Sunday in April (the one on or after April 24) and to end at 2am (DST time) -# on the last Sunday of October, which is the first Sunday -# on or after Oct 25. -DSTSTART_1967_1986 = datetime(1, 4, 24, 2) -DSTEND_1967_1986 = DSTEND_1987_2006 - -def us_dst_range(year): - # Find start and end times for US DST. For years before 1967, return - # start = end for no DST. - if 2006 < year: - dststart, dstend = DSTSTART_2007, DSTEND_2007 - elif 1986 < year < 2007: - dststart, dstend = DSTSTART_1987_2006, DSTEND_1987_2006 - elif 1966 < year < 1987: - dststart, dstend = DSTSTART_1967_1986, DSTEND_1967_1986 - else: - return (datetime(year, 1, 1), ) * 2 - - start = first_sunday_on_or_after(dststart.replace(year=year)) - end = first_sunday_on_or_after(dstend.replace(year=year)) - return start, end - - -class USTimeZone(tzinfo): - - def __init__(self, hours, reprname, stdname, dstname): - self.stdoffset = timedelta(hours=hours) - self.reprname = reprname - self.stdname = stdname - self.dstname = dstname - - def __repr__(self): - return self.reprname - - def tzname(self, dt): - if self.dst(dt): - return self.dstname - else: - return self.stdname - - def utcoffset(self, dt): - return self.stdoffset + self.dst(dt) - - def dst(self, dt): - if dt is None or dt.tzinfo is None: - # An exception may be sensible here, in one or both cases. - # It depends on how you want to treat them. The default - # fromutc() implementation (called by the default astimezone() - # implementation) passes a datetime with dt.tzinfo is self. - return ZERO - assert dt.tzinfo is self - start, end = us_dst_range(dt.year) - # Can't compare naive to aware objects, so strip the timezone from - # dt first. - dt = dt.replace(tzinfo=None) - if start + HOUR <= dt < end - HOUR: - # DST is in effect. - return HOUR - if end - HOUR <= dt < end: - # Fold (an ambiguous hour): use dt.fold to disambiguate. - return ZERO if dt.fold else HOUR - if start <= dt < start + HOUR: - # Gap (a non-existent hour): reverse the fold rule. - return HOUR if dt.fold else ZERO - # DST is off. - return ZERO - - def fromutc(self, dt): - assert dt.tzinfo is self - start, end = us_dst_range(dt.year) - start = start.replace(tzinfo=self) - end = end.replace(tzinfo=self) - std_time = dt + self.stdoffset - dst_time = std_time + HOUR - if end <= dst_time < end + HOUR: - # Repeated hour - return std_time.replace(fold=1) - if std_time < start or dst_time >= end: - # Standard time - return std_time - if start <= std_time < end - HOUR: - # Daylight saving time - return dst_time - - -Eastern = USTimeZone(-5, "Eastern", "EST", "EDT") -Central = USTimeZone(-6, "Central", "CST", "CDT") -Mountain = USTimeZone(-7, "Mountain", "MST", "MDT") -Pacific = USTimeZone(-8, "Pacific", "PST", "PDT") diff --git a/Doc/includes/tzinfo_examples.py b/Doc/includes/tzinfo_examples.py new file mode 100644 index 0000000..ae5a509 --- /dev/null +++ b/Doc/includes/tzinfo_examples.py @@ -0,0 +1,175 @@ +from datetime import tzinfo, timedelta, datetime, timezone + +ZERO = timedelta(0) +HOUR = timedelta(hours=1) +SECOND = timedelta(seconds=1) + +# A class capturing the platform's idea of local time. +# (May result in wrong values on historical times in +# timezones where UTC offset and/or the DST rules had +# changed in the past.) +import time as _time + +STDOFFSET = timedelta(seconds = -_time.timezone) +if _time.daylight: + DSTOFFSET = timedelta(seconds = -_time.altzone) +else: + DSTOFFSET = STDOFFSET + +DSTDIFF = DSTOFFSET - STDOFFSET + +class LocalTimezone(tzinfo): + + def fromutc(self, dt): + assert dt.tzinfo is self + stamp = (dt - datetime(1970, 1, 1, tzinfo=self)) // SECOND + args = _time.localtime(stamp)[:6] + dst_diff = DSTDIFF // SECOND + # Detect fold + fold = (args == _time.localtime(stamp - dst_diff)) + return datetime(*args, microsecond=dt.microsecond, + tzinfo=self, fold=fold) + + def utcoffset(self, dt): + if self._isdst(dt): + return DSTOFFSET + else: + return STDOFFSET + + def dst(self, dt): + if self._isdst(dt): + return DSTDIFF + else: + return ZERO + + def tzname(self, dt): + return _time.tzname[self._isdst(dt)] + + def _isdst(self, dt): + tt = (dt.year, dt.month, dt.day, + dt.hour, dt.minute, dt.second, + dt.weekday(), 0, 0) + stamp = _time.mktime(tt) + tt = _time.localtime(stamp) + return tt.tm_isdst > 0 + +Local = LocalTimezone() + + +# A complete implementation of current DST rules for major US time zones. + +def first_sunday_on_or_after(dt): + days_to_go = 6 - dt.weekday() + if days_to_go: + dt += timedelta(days_to_go) + return dt + + +# US DST Rules +# +# This is a simplified (i.e., wrong for a few cases) set of rules for US +# DST start and end times. For a complete and up-to-date set of DST rules +# and timezone definitions, visit the Olson Database (or try pytz): +# http://www.twinsun.com/tz/tz-link.htm +# http://sourceforge.net/projects/pytz/ (might not be up-to-date) +# +# In the US, since 2007, DST starts at 2am (standard time) on the second +# Sunday in March, which is the first Sunday on or after Mar 8. +DSTSTART_2007 = datetime(1, 3, 8, 2) +# and ends at 2am (DST time) on the first Sunday of Nov. +DSTEND_2007 = datetime(1, 11, 1, 2) +# From 1987 to 2006, DST used to start at 2am (standard time) on the first +# Sunday in April and to end at 2am (DST time) on the last +# Sunday of October, which is the first Sunday on or after Oct 25. +DSTSTART_1987_2006 = datetime(1, 4, 1, 2) +DSTEND_1987_2006 = datetime(1, 10, 25, 2) +# From 1967 to 1986, DST used to start at 2am (standard time) on the last +# Sunday in April (the one on or after April 24) and to end at 2am (DST time) +# on the last Sunday of October, which is the first Sunday +# on or after Oct 25. +DSTSTART_1967_1986 = datetime(1, 4, 24, 2) +DSTEND_1967_1986 = DSTEND_1987_2006 + +def us_dst_range(year): + # Find start and end times for US DST. For years before 1967, return + # start = end for no DST. + if 2006 < year: + dststart, dstend = DSTSTART_2007, DSTEND_2007 + elif 1986 < year < 2007: + dststart, dstend = DSTSTART_1987_2006, DSTEND_1987_2006 + elif 1966 < year < 1987: + dststart, dstend = DSTSTART_1967_1986, DSTEND_1967_1986 + else: + return (datetime(year, 1, 1), ) * 2 + + start = first_sunday_on_or_after(dststart.replace(year=year)) + end = first_sunday_on_or_after(dstend.replace(year=year)) + return start, end + + +class USTimeZone(tzinfo): + + def __init__(self, hours, reprname, stdname, dstname): + self.stdoffset = timedelta(hours=hours) + self.reprname = reprname + self.stdname = stdname + self.dstname = dstname + + def __repr__(self): + return self.reprname + + def tzname(self, dt): + if self.dst(dt): + return self.dstname + else: + return self.stdname + + def utcoffset(self, dt): + return self.stdoffset + self.dst(dt) + + def dst(self, dt): + if dt is None or dt.tzinfo is None: + # An exception may be sensible here, in one or both cases. + # It depends on how you want to treat them. The default + # fromutc() implementation (called by the default astimezone() + # implementation) passes a datetime with dt.tzinfo is self. + return ZERO + assert dt.tzinfo is self + start, end = us_dst_range(dt.year) + # Can't compare naive to aware objects, so strip the timezone from + # dt first. + dt = dt.replace(tzinfo=None) + if start + HOUR <= dt < end - HOUR: + # DST is in effect. + return HOUR + if end - HOUR <= dt < end: + # Fold (an ambiguous hour): use dt.fold to disambiguate. + return ZERO if dt.fold else HOUR + if start <= dt < start + HOUR: + # Gap (a non-existent hour): reverse the fold rule. + return HOUR if dt.fold else ZERO + # DST is off. + return ZERO + + def fromutc(self, dt): + assert dt.tzinfo is self + start, end = us_dst_range(dt.year) + start = start.replace(tzinfo=self) + end = end.replace(tzinfo=self) + std_time = dt + self.stdoffset + dst_time = std_time + HOUR + if end <= dst_time < end + HOUR: + # Repeated hour + return std_time.replace(fold=1) + if std_time < start or dst_time >= end: + # Standard time + return std_time + if start <= std_time < end - HOUR: + # Daylight saving time + return dst_time + + +Eastern = USTimeZone(-5, "Eastern", "EST", "EDT") +Central = USTimeZone(-6, "Central", "CST", "CDT") +Mountain = USTimeZone(-7, "Mountain", "MST", "MDT") +Pacific = USTimeZone(-8, "Pacific", "PST", "PDT") diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index c931855..700a19d 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -1221,7 +1221,7 @@ Instance methods: >>> from datetime import datetime - >>> datetime.now().isoformat(timespec='minutes') + >>> datetime.now().isoformat(timespec='minutes') # doctest: +SKIP '2002-12-25T00:00' >>> dt = datetime(2015, 1, 1, 12, 30, 59, 0) >>> dt.isoformat(timespec='microseconds') @@ -1781,9 +1781,11 @@ There is one more :class:`tzinfo` method that a subclass may wish to override: else: return dt -Example :class:`tzinfo` classes: +In the following :download:`tzinfo_examples.py +<../includes/tzinfo_examples.py>` file there are some examples of +:class:`tzinfo` classes: -.. literalinclude:: ../includes/tzinfo-examples.py +.. literalinclude:: ../includes/tzinfo_examples.py Note that there are unavoidable subtleties twice per year in a :class:`tzinfo` subclass accounting for both standard and daylight time, at the DST transition @@ -1804,6 +1806,8 @@ When DST starts (the "start" line), the local wall clock leaps from 1:59 to ``astimezone(Eastern)`` won't deliver a result with ``hour == 2`` on the day DST begins. For example, at the Spring forward transition of 2016, we get + >>> from datetime import datetime, timezone + >>> from tzinfo_examples import HOUR, Eastern >>> u0 = datetime(2016, 3, 13, 5, tzinfo=timezone.utc) >>> for i in range(4): ... u = u0 + i*HOUR -- cgit v0.12