diff --git a/pep-0495.txt b/pep-0495.txt index 7423ccb37..fbe93b94a 100644 --- a/pep-0495.txt +++ b/pep-0495.txt @@ -72,28 +72,170 @@ Methods The ``replace()`` methods of the ``datetime.time`` and ``datetime.datetime`` classes will get a new keyword-only argument -called ``first`` with the default value ``True``. The value of the -``first`` argument will be used to set the value of the ``first`` -attribute in the returned instance. +called ``first`` with the default value ``True``. It will +becave similarly to the other ``replace()`` arguments: if the ``first`` +argument is specified and given a boolean value, the new instance +returned by ``replace()`` will have its ``first`` attribute set +to that value. In CPython, a non-boolean value of ``first`` will +raise a ``TypeError``, but other implementations may allow the value +``None`` to behave the same as when ``first`` is not given. If the +``first`` argument is not specified, the original value of the ``first`` +attribute is copied to the result. Affected Behaviors ------------------ -The ``timestamp()`` method of ``datetime.datetime`` will return value -advanced by 3600 if ``self`` represents an ambiguous hour and -``first`` is False. +Conversion from naive to aware +.............................. + +The ``astimezone()`` method will now work for naive ``self``. The +system local timezone will be assumed in this case and the ``first`` +flag will be used to determine which local timezone is in effect +in the ambiguous case. + +For example, on a system set to US/Eastern timezone:: + + >>> dt = datetime(2014, 11, 2, 1, 30) + >>> dt.astimezone().strftime('%D %T %Z%z') + '11/02/14 01:30:00 EDT-0400' + >>> dt.replace(first=False).astimezone().strftime('%D %T %Z%z') + '11/02/14 01:30:00 EST-0500' + +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 value. + +When a ``datetime.datetime`` instance ``dt`` represents an ambiguous +(repeated) time, there are two values ``s0`` and ``s1`` such that:: + + datetime.fromtimestamp(s0) == datetime.fromtimestamp(s1) == dt + +In this case, ``dt.timestamp()`` will return the smaller of ``s0`` +and ``s1`` values if ``dt.first == True`` and the larger otherwise. + + +For example, on a system set to US/Eastern timezone:: + + >>> datetime(2014, 11, 2, 1, 30, first=True).timestamp() + 1414906200.0 + >>> datetime(2014, 11, 2, 1, 30, first=False).timestamp() + 1414909800.0 + + +When a ``datetime.datetime`` instance ``dt`` represents an invalid +time, there is no value ``s`` for which:: + + datetime.fromtimestamp(s) == dt + +but we can form two "nice to know" values of ``s`` that differ +by the size of the gap in seconds. One is the value of ``s`` +that would correspond to ``dt`` in a timezone where the UTC offset +is always the same as the offset right before the gap and the +other is the similar value but in a timezone the UTC offset +is always the same as the offset right after the gap. + +The value returned by ``dt.timestamp()`` given the invalid +``dt`` will be the larger of the two "nice to know" values +if ``dt.first == True`` and the larger otherwise. + +For example, on a system set to US/Eastern timezone:: + + >>> datetime(2015, 3, 8, 2, 30, first=True).timestamp() + 1425799800.0 + >>> datetime(2015, 3, 8, 2, 30, first=False).timestamp() + 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) + + +Implementations of tzinfo in stdlib +................................... + +No new implementations of ``datetime.tzinfo`` abstract class are +introduced 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``. + +New guidelines will be published for implementing concrete timezones +with variable UTC offset. + + +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 +these guidelines. + +New subclasses should override the baseclass ``fromutc()`` method so +that in all cases where two UTC times ``u1`` and ``u2`` (``u1`` <``u2``) +corespond to the same local time ``fromutc(u1)`` will return an instance +with ``first=True`` and ``fromutc(u1)`` will return an instance +with ``first=False``. + +New implementations of ``utcoffset()`` and ``dst()`` methods should +ignore the value of ``first`` unless they are called on the ambiguous +or invalid times. + +On an ambiguous time introduced at the end of DST, the values returned +by ``utcoffset()`` and ``dst()`` methods should be as follows + ++-----------------+----------------+------------------+ +| | first=True | first=False | ++-----------------+----------------+------------------+ +| utcoff() | stdoff + hour | stdoff | ++-----------------+----------------+------------------+ +| dst() | hour | zero | ++-----------------+----------------+------------------+ + +where ``stdoff`` is the standard (non-DST) offset, +``hour = timedelta(hours=1)`` and ``zero = timedelta(0)``. + +On an invalid 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 | ++-----------------+----------------+------------------+ +| dst() | zero | hour | ++-----------------+----------------+------------------+ + + +On ambiguous/invalid 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 +according to the following table: + ++-----------------+----------------+-----------------------------+ +| | first=True | first=False | ++-----------------+----------------+-----------------------------+ +| ambiguous | oldoff | newoff = oldoff - delta | ++-----------------+----------------+-----------------------------+ +| invalid | oldoff | newoff = oldoff + delta | ++-----------------+----------------+-----------------------------+ -Implementations of tzinfo -......................... -Subclasses of ``datetime.tzinfo`` will read the values of ``first`` in -``utcoffset()`` and ``dst()`` methods and set it appropriately in the -instances returned by the ``fromutc()`` method. No change to the -signatures of these methods is proposed. Pickle size ----------- @@ -117,7 +259,7 @@ Temporal Arithmetics -------------------- The value of "first" will be ignored in all operations except those -that involve conversion between timezones. +that involve conversion between timezones. [#]_ The result of addition (subtraction) of a timedelta to (from) a datetime will always have ``first`` set to ``True`` even if the @@ -125,9 +267,17 @@ original datetime instance had ``first=False``. (The only methods that will be able to produce non-default value of "first" are ``__new__``, and ``replace()`` methods of the -``datetime.datetime`` and ``datetime.time`` classes ``now()`` and -``fromtimestamp()`` methods of the ``datetime.datetime`` class, and -``fromutc()`` method of some tzinfo implementations.) +``datetime.datetime`` and ``datetime.time`` classes ``now()``, +``astimezone()`` and ``fromtimestamp()`` methods of the +``datetime.datetime`` class, and ``fromutc()`` method of some tzinfo +implementations.) + + +.. [#] 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. Comparison ----------