[Python-checkins] peps: PEP 495: Addressed several (not all yet) mailing list commments.
alexander.belopolsky
python-checkins at python.org
Sat Aug 22 04:28:28 CEST 2015
https://hg.python.org/peps/rev/3bf478b656e6
changeset: 5969:3bf478b656e6
user: Alexander Belopolsky <alexander.belopolsky at gmail.com>
date: Fri Aug 21 22:28:19 2015 -0400
summary:
PEP 495: Addressed several (not all yet) mailing list commments.
files:
pep-0495.txt | 277 +++++++++++++++++++++++++-------------
1 files changed, 181 insertions(+), 96 deletions(-)
diff --git a/pep-0495.txt b/pep-0495.txt
--- a/pep-0495.txt
+++ b/pep-0495.txt
@@ -21,15 +21,15 @@
.. sidebar:: US public service advertisement
.. image:: pep-0495-daylightsavings.png
- :align: right
- :width: 15%
+ :align: center
+ :width: 95%
Rationale
=========
In the most world locations there have been and will be times when
-local clocks are moved back. In those times, intervals are introduced
+local clocks are moved back. [#]_ In those times, intervals are introduced
in which local clocks show the same time twice in the same day. In
these situations, the information displayed on a local clock (or
stored in a Python datetime instance) is insufficient to identify a
@@ -37,14 +37,27 @@
flag to the ``datetime`` instances that will distinguish between the
two ambiguous times.
+.. [#] People who live in locations observing the Daylight Saving
+ Time (DST) move their clocks back (usually one hour) every Fall.
+
+ It is less common, but occasionally clocks can be moved back for
+ other reasons. For example, Ukraine skipped the spring-forward
+ transition in March 1990 and instead, moved their clocks back on
+ July 1, 1990, switching from Moscow Time to Eastern European Time.
+ In that case, standard (winter) time was in effect before and after
+ the transition.
+
+ Both DST and standard time changes may result in time shifts other
+ than an hour.
+
Terminology
===========
-When clocks are moved back, we say that a *fold* is created in the
-fabric of time. When the clock are moved forward, a *gap* is created.
-A local time that falls in the fold is called *ambiguous*. A local
-time that falls in the gap is called *missing*.
+When clocks are moved back, we say that a *fold* is created in time.
+When the clocks are moved forward, a *gap* is created. A local time
+that falls in the fold is called *ambiguous*. A local time that falls
+in the gap is called *missing*.
Proposal
@@ -55,7 +68,7 @@
We propose adding a boolean member called ``first`` to the instances
of ``datetime.time`` and ``datetime.datetime`` classes. This member
-should have the value True for all instances except those that
+should have the value ``True`` for all instances except those that
represent the second (chronologically) moment in time in an ambiguous
case. [#]_
@@ -143,6 +156,18 @@
Affected Behaviors
------------------
+What time is it?
+................
+
+The ``datetime.now()`` method called with no arguments, will set
+``first=False`` when returning the second of the two ambiguous times
+in a fold. When called with a ``tzinfo`` argument, the value of the
+``first`` will be determined by the ``tzinfo.fromutc()``
+implementation. If an instance of the built-in ``datetime.timezone``
+is passed as ``tzinfo``, the returned datetime instance will always
+have ``first=True``.
+
+
Conversion from naive to aware
..............................
@@ -159,16 +184,31 @@
>>> dt.replace(first=False).astimezone().strftime('%D %T %Z%z')
'11/02/14 01:30:00 EST-0500'
+
+Conversion from POSIX seconds from EPOCH
+........................................
+
+The ``fromtimestamp()`` static method of ``datetime.datetime`` will
+set the ``first`` attribute appropriately in the returned object.
+
+For example, on a system set to US/Eastern timezone::
+
+ >>> datetime.fromtimestamp(1414906200)
+ datetime.datetime(2014, 11, 2, 1, 30)
+ >>> datetime.fromtimestamp(1414906200 + 3600)
+ datetime.datetime(2014, 11, 2, 1, 30, first=False)
+
+
Conversion to POSIX seconds from EPOCH
......................................
The ``timestamp()`` method of ``datetime.datetime`` will return different
values for ``datetime.datetime`` instances that differ only by the value
of their ``first`` attribute if and only if these instances represent an
-ambiguous or a non-existent time.
+ambiguous or a missing time.
When a ``datetime.datetime`` instance ``dt`` represents an ambiguous
-(repeated) time, there are two values ``s0`` and ``s1`` such that::
+time, there are two values ``s0`` and ``s1`` such that::
datetime.fromtimestamp(s0) == datetime.fromtimestamp(s1) == dt
@@ -208,21 +248,6 @@
1425796200.0
-Conversion from POSIX seconds from EPOCH
-........................................
-
-
-The ``fromtimestamp()`` static method of ``datetime.datetime`` will
-set the ``first`` attribute appropriately in the returned object.
-
-For example, on a system set to US/Eastern timezone::
-
- >>> datetime.fromtimestamp(1414906200)
- datetime.datetime(2014, 11, 2, 1, 30)
- >>> datetime.fromtimestamp(1414906200 + 3600)
- datetime.datetime(2014, 11, 2, 1, 30, first=False)
-
-
Combining and splitting date and time
.....................................
@@ -233,11 +258,27 @@
``first`` attribute to the resulting ``datetime.time`` instance.
+Pickles
+.......
+
+Pickle sizes for the ``datetime.datetime`` and ``datetime.time``
+objects will not change. The ``first`` flag will be encoded in the
+first bit of the 5th byte of the ``datetime.datetime`` pickle payload
+or the 2nd byte of the datetime.time. In the `current implementation`_
+these bytes are used to store minute value (0-59) and the first bit is
+always 0. Note that ``first=True`` will be encoded as 0 in the first
+bit and ``first=False`` as 1. (This change only affects pickle
+format. In the C implementation, the "first" member will get a full byte
+to store the actual boolean value.)
+
+
+.. _current implementation: https://hg.python.org/cpython/file/d3b20bff9c5d/Include/datetime.h#l17
+
Implementations of tzinfo in stdlib
-...................................
+===================================
No new implementations of ``datetime.tzinfo`` abstract class are
-introduced in this PEP. The existing (fixed offset) timezones do
+proposed in this PEP. The existing (fixed offset) timezones do
not introduce ambiguous local times and their ``utcoffset()``
implementation will return the same constant value as they do now
regardless of the value of ``first``.
@@ -248,27 +289,32 @@
implementation (the ``datetime.timzeone`` class implementing fixed
offset timezones) override ``fromutc()``.
-New guidelines will be published for implementing concrete timezones
-with variable UTC offset.
-
-Guidelines for new tzinfo implementations
------------------------------------------
+Guidelines for New tzinfo Implementations
+=========================================
Implementors of concrete ``datetime.tzinfo`` subclasses who want to
-support variable UTC offsets (due to DST and other causes) must follow
+support variable UTC offsets (due to DST and other causes) should follow
these guidelines.
-New subclasses must override the base-class ``fromutc()`` method and
+
+Ignorance is Bliss
+------------------
+
+New implementations of ``utcoffset()``, ``tzname()`` and ``dst()``
+methods should ignore the value of ``first`` unless they are called on
+the ambiguous or missing times.
+
+
+In the DST Fold
+---------------
+
+New subclasses should override the base-class ``fromutc()`` method and
implement it so that in all cases where two UTC times ``u1`` and
``u2`` (``u1`` <``u2``) correspond to the same local time
``fromutc(u1)`` will return an instance with ``first=True`` and
``fromutc(u2)`` will return an instance with ``first=False``. In all
-other cases the returned instance must have ``first=True``.
-
-New implementations of ``utcoffset()`` and ``dst()`` methods should
-ignore the value of ``first`` unless they are called on the ambiguous
-or missing times.
+other cases the returned instance should have ``first=True``.
On an ambiguous time introduced at the end of DST, the values returned
by ``utcoffset()`` and ``dst()`` methods should be as follows
@@ -276,30 +322,37 @@
+-----------------+----------------+------------------+
| | first=True | first=False |
+=================+================+==================+
-| utcoff() | stdoff + hour | stdoff |
+| utcoffset() | stdoff + dstoff| stdoff |
+-----------------+----------------+------------------+
-| dst() | hour | zero |
+| dst() | dstoff | zero |
+-----------------+----------------+------------------+
-where ``stdoff`` is the standard (non-DST) offset,
-``hour = timedelta(hours=1)`` and ``zero = timedelta(0)``.
+where ``stdoff`` is the standard (non-DST) offset, ``dstoff`` is the
+DST correction (typically ``dstoff = timedelta(hours=1)``) and ``zero
+= timedelta(0)``.
+
+
+Mind the DST Gap
+----------------
On a missing time introduced at the start of DST, the values returned
by ``utcoffset()`` and ``dst()`` methods should be as follows
-
+-----------------+----------------+------------------+
| | first=True | first=False |
+=================+================+==================+
-| utcoff() | stdoff | stdoff + hour |
+| utcoffset() | stdoff | stdoff + dstoff |
+-----------------+----------------+------------------+
-| dst() | zero | hour |
+| dst() | zero | dstoff |
+-----------------+----------------+------------------+
+Non-DST Folds and Gaps
+----------------------
+
On ambiguous/missing times introduced by the change in the standard time
offset, the ``dst()`` method should return the same value regardless of
-the value of ``first`` and the ``utcoff()`` should return values
+the value of ``first`` and the ``utcoffset()`` should return values
according to the following table:
+-----------------+----------------+-----------------------------+
@@ -310,28 +363,11 @@
| missing | oldoff | newoff = oldoff + delta |
+-----------------+----------------+-----------------------------+
+where ``delta`` is the size of the fold or the gap.
-Pickle size
------------
-
-Pickle sizes for the ``datetime.datetime`` and ``datetime.time``
-objects will not change. The ``first`` flag will be encoded in the
-first bit of the 5th byte of the ``datetime.datetime`` pickle payload
-or the 2nd byte of the datetime.time. In the `current implementation`_
-these bytes are used to store minute value (0-59) and the first bit is
-always 0. Note that ``first=True`` will be encoded as 0 in the first
-bit and ``first=False`` as 1. (This change only affects pickle
-format. In C implementation, the "first" member will get a full byte
-to store the actual boolean value.)
-
-We chose the minute byte to store the the "first" bit because this
-choice preserves the natural ordering.
-
-.. _current implementation: https://hg.python.org/cpython/file/d3b20bff9c5d/Include/datetime.h#l17
-
-Temporal Arithmetics
---------------------
+Temporal Arithmetic
+===================
The value of "first" will be ignored in all operations except those
that involve conversion between timezones. [#]_ As a consequence,
@@ -345,15 +381,16 @@
datetime will always have ``first`` set to ``True`` even if the
original datetime instance had ``first=False``.
-.. [#] As of Python 3.5, ``tzinfo`` is ignored whenever timedelta is
- added or subtracted from a ``datetime.datetime`` instance or when
- one ``datetime.datetime`` instance is subtracted from another with
- the same (even not-None) ``tzinfo``. This may change in the future,
- but such changes are outside of the scope of this PEP.
+.. [#] Computing a difference between two aware datetime instances
+ with different values of ``tzinfo`` involves an implicit timezone
+ conversion. In this case, the result may depend on the value of
+ the ``first`` flag in either of the instances, but only if the
+ instance has ``tzinfo`` that accounts for the value of ``first``
+ in its ``utcoffset()`` method.
Backward and Forward Compatibility
-----------------------------------
+==================================
This proposal will have little effect on the programs that do not read
the ``first`` flag explicitly or use tzinfo implementations that do.
@@ -368,16 +405,19 @@
versions. Pickles of instances with ``first=True`` (which is the
default) will remain unchanged.
+
Questions and Answers
=====================
-1. Why not call the new flag "isdst"?
+Why not call the new flag "isdst"?
+----------------------------------
--------
+A non-technical answer
+......................
-* Alice: Bob - let's have a stargazing party at 01:30 AM tomorrow!
-* Bob: Should I presume initially that summer time (for example, Daylight
- Saving Time) is or is not (respectively) in effect for the specified time?
+* Alice: Bob - let's have a stargazing party at 01:30 AM tomorrow!
+* Bob: Should I presume initially that Daylight Saving Time is or is
+ not in effect for the specified time?
* Alice: Huh?
-------
@@ -386,32 +426,77 @@
* Alice: You know, Bob, 01:30 AM will happen twice tomorrow. Which time do you have in mind?
* Bob: I did not think about it, but let's pick the first.
+-------
-2. Why "first"?
+A technical reason
+..................
-* Rejections:
+While the ``tm_isdst`` field of the ``time.struct_time`` object can be
+used to disambiguate local times in the fold, the semantics of such
+disambiguation are completely different from the proposal in this PEP.
- **second**
- rejected because "second" is already there.
+The main problem with the ``tm_isdst`` field is that it is impossible
+to know what value is appropriate for ``tm_isdst`` without knowing the
+details about the time zone that are only available to the ``tzinfo``
+implementation. Thus while ``tm_isdst`` is useful in the *output* of
+methods such as ``time.localtime``, it is cumbursome as an *input* of
+methods such as ``time.mktime``.
+
+If the programmer misspecifies a non-negative value of ``tm_isdst`` to
+``time.mktime``, the result will be time that is 1 hour off and since
+there is rarely a way to know anything about DST *before* a call to
+``time.mktime`` is made, the only sane choice is usually
+``tm_isdst=-1``.
+
+Unlike ``tm_isdst``, the proposed ``first`` flag has no effect on the
+interpretation of the datetime instance unless without that flag two
+(or no) interpretations are possible.
+
+Since it would be very confusing to have something called ``isdst``
+that does not have the same semantics as ``tm_isdst``, we need a
+different name. Moreover, the ``datetime.datetime`` class already has
+a method called ``dst()`` and if we called ``first`` "isdst", we would
+necessarily have situations when "isdst" and ``bool(dst())`` values
+are different.
+
+
+Why "first"?
+------------
+
+This is a working name chosen initially because the obvious
+alternative ("second") conflicts with the existing attribute. It has
+since became clear that it is desirable to have a flag with the
+default value ``False`` and such that chronological ordering of
+disambiguated (datetime, flag) pairs would match their lexicographical
+order.
+
+The following alternative names have been proposed:
+
+ **fold**
+ Suggested by Guido van Rossum and favored by the author. Has
+ correct connotations and easy mnemonic rules, but at the same
+ time does not invite unbased assumptions.
**later**
- rejected because "later" is confusable with "latter".
-
- **earlier**
- rejected because "earlier" has the same issue as "first" (requires
- default to be True) but is two characters longer.
-
-* Remaining possibilities:
-
+ A close contender to "fold". The author dislikes it because
+ it is confusable with equally fitting "latter," but in the age
+ of autocompletion everywhere this is a small consideration. A
+ stronger objection may be that in the case of missing time, we
+ will have ``later=True`` instance converted to an earlier time by
+ ``.astimezone(timezone.utc)`` that that with ``later=False``.
+ Yet again, this can be interpreted as a desirable indication that
+ the original time is invalid.
+
**repeated**
- this is a strong candidate
-
- **is_first**
- arguably more grammatically correct than "first"
+ Did not receive any support on the mailing list.
**ltdf**
- (Local Time Disambiguation Flag) - short and no-one will
- attempt to guess what it means without reading the docs.
+ (Local Time Disambiguation Flag) - short and no-one will attempt
+ to guess what it means without reading the docs. (Feel free to
+ use it in discussions with the meaning ltdf=False is the
+ earlier if you don't want to endorse any of the alternatives
+ above.)
+
Implementation
==============
--
Repository URL: https://hg.python.org/peps
More information about the Python-checkins
mailing list