[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