[Python-checkins] peps: PEP 495: Are two values enough?
alexander.belopolsky
python-checkins at python.org
Sat Aug 22 23:24:20 CEST 2015
https://hg.python.org/peps/rev/1d8bc4791634
changeset: 5975:1d8bc4791634
user: Alexander Belopolsky <alexander.belopolsky at gmail.com>
date: Sat Aug 22 17:24:15 2015 -0400
summary:
PEP 495: Are two values enough?
files:
pep-0495.txt | 112 +++++++++++++++++++++++++++++++++++++-
1 files changed, 107 insertions(+), 5 deletions(-)
diff --git a/pep-0495.txt b/pep-0495.txt
--- a/pep-0495.txt
+++ b/pep-0495.txt
@@ -396,8 +396,8 @@
the ``first`` flag explicitly or use tzinfo implementations that do.
The only visible change for such programs will be that conversions to
and from POSIX timestamps will now round-trip correctly (up to
-floating point rounding). Programs that implemented work-arounds to
-the old incorrect behavior will need to be modified.
+floating point rounding). Programs that implemented a work-around to
+the old incorrect behavior may need to be modified.
Pickles produced by older programs will remain fully forward
compatible. Only datetime/time instances with ``first=False`` pickled
@@ -439,10 +439,10 @@
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.localtime``, it is cumbersome as an *input* of
methods such as ``time.mktime``.
-If the programmer misspecifies a non-negative value of ``tm_isdst`` to
+If the programmer misspecified 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
@@ -480,7 +480,7 @@
**later**
A close contender to "fold". One author dislikes it because
it is confusable with equally fitting "latter," but in the age
- of autocompletion everywhere this is a small consideration. A
+ of auto-completion 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``.
@@ -498,6 +498,108 @@
above.)
+Are two values enough?
+---------------------------------------
+
+The ``time.mktime`` interface allows three values for the ``tm_isdst``
+flag: -1, 0, and 1. As we explained above, -1 (asking ``mktime`` to
+determine whether DST is in effect for the given time from the rest of
+the fields) is the only choice that is useful in practice.
+
+With the ``first`` flag, however, ``datetime.timestamp()`` will return
+the same value as ``mktime`` with ``tm_isdst=-1`` in 99.98% of the
+time for most time zones with DST transitions. Moreover,
+``tm_isdst=-1``-like behavior is specified *regardless* of the value
+of ``first``.
+
+It is only in the 0.02% cases (2 hours per year) that the
+``datetime.timestamp()`` and ``mktime`` with ``tm_isdst=-1`` may
+disagree. However, even in this case, most of the ``mktime``
+implementations will return the ``first=True`` or the ``first=False``
+value even though relevant standards allow ``mktime`` to return -1 and
+set an error code in those cases.
+
+In other words, ``tm_isdst=-1`` behavior is not missing from this PEP.
+To the contrary, it is the only behavior provided in two different
+well-defined flavors. The behavior that is missing is when a given
+local hour is interpreted as a different local hour because of the
+misspecified ``tm_isdst``.
+
+For example, in the DST-observing time zones in the Northern
+hemisphere (where DST is in effect in June) one can get
+
+.. code::
+
+ >>> from time import mktime, localtime
+ >>> t = mktime((2015, 6, 1, 12, 0, 0, -1, -1, 0))
+ >>> localtime(t)[:]
+ (2015, 6, 1, 13, 0, 0, 0, 152, 1)
+
+Note that 12:00 was interpreted as 13:00 by ``mktime``. With the
+``datetime.timestamp``, ``datetime.fromtimestamp``, it is currently
+guaranteed that
+
+.. code::
+
+ >>> t = datetime.datetime(2015, 6, 1, 12).timestamp()
+ >>> datetime.datetime.fromtimestamp(t)
+ datetime.datetime(2015, 6, 1, 12, 0)
+
+This PEP extends the same guarantee to both values of ``first``:
+
+.. code::
+
+ >>> t = datetime.datetime(2015, 6, 1, 12, first=True).timestamp()
+ >>> datetime.datetime.fromtimestamp(t)
+ datetime.datetime(2015, 6, 1, 12, 0)
+
+.. code::
+
+ >>> t = datetime.datetime(2015, 6, 1, 12, first=False).timestamp()
+ >>> datetime.datetime.fromtimestamp(t)
+ datetime.datetime(2015, 6, 1, 12, 0)
+
+Thus one of the suggested uses for ``first=-1`` -- to match the legacy
+behavior -- is not needed. Either choice of ``first`` will match the
+old behavior except in the few cases where the old behavior was
+undefined.
+
+Another suggestion was to use ``first=-1`` or ``first=None`` to
+indicate that the program truly has no means to deal with the folds
+and gaps and ``dt.utcoffset()`` should raise an error whenever ``dt``
+represents an ambiguous or missing local time.
+
+The main problem with this proposal, is that ``dt.utcoffset()`` is
+used internally in situations where raising an error is not an option:
+for example, in dictionary lookups or list/set membership checks. So
+strict gap/fold checking behavior would need to be controlled by a
+separate flag, say ``dt.utcoffset(raise_on_gap=True,
+raise_on_fold=False)``. However, this functionality can be easily
+implemented in user code:
+
+.. code::
+
+ def utcoffset(dt, raise_on_gap=True, raise_on_fold=False):
+ u = dt.utcoffset()
+ v = dt.replace(first=not dt.first).utcoffset()
+ if u == v:
+ return u
+ if (u < v) == dt.first:
+ if raise_on_fold:
+ raise AmbiguousTimeError
+ else:
+ if raise_on_gap:
+ raise MissingTimeError
+ return u
+
+Moreover, raising an error in the problem cases is only one of many
+possible solutions. An interactive program can ask the user for
+additional input, while a server process may log a warning and take an
+appropriate default action. We cannot possibly provide functions for
+all possible user requirements, but this PEP provides the means to
+implement any desired behavior in a few lines of code.
+
+
Implementation
==============
--
Repository URL: https://hg.python.org/peps
More information about the Python-checkins
mailing list