204 lines
6.9 KiB
ReStructuredText
204 lines
6.9 KiB
ReStructuredText
PEP: 500
|
|
Title: A protocol for delegating datetime methods to their tzinfo implementations
|
|
Version: $Revision$
|
|
Last-Modified: $Date$
|
|
Author: Alexander Belopolsky <alexander.belopolsky@gmail.com>, Tim Peters <tim.peters@gmail.com>
|
|
Discussions-To: datetime-sig@python.org
|
|
Status: Rejected
|
|
Type: Standards Track
|
|
Content-Type: text/x-rst
|
|
Requires: 495
|
|
Created: 08-Aug-2015
|
|
Resolution: https://mail.python.org/pipermail/datetime-sig/2015-August/000354.html
|
|
|
|
Abstract
|
|
========
|
|
|
|
This PEP specifies a new protocol (PDDM - "A Protocol for Delegating
|
|
Datetime Methods") that can be used by concrete implementations of the
|
|
``datetime.tzinfo`` interface to override aware datetime arithmetics,
|
|
formatting and parsing. We describe changes to the
|
|
``datetime.datetime`` class to support the new protocol and propose a
|
|
new abstract class ``datetime.tzstrict`` that implements parts of this
|
|
protocol necessary to make aware datetime instances to follow "strict"
|
|
arithmetic rules.
|
|
|
|
|
|
Rationale
|
|
=========
|
|
|
|
As of Python 3.5, aware datetime instances that share a ``tzinfo``
|
|
object follow the rules of arithmetics that are induced by a simple
|
|
bijection between (year, month, day, hour, minute, second,
|
|
microsecond) 7-tuples and large integers. In this arithmetics, the
|
|
difference between YEAR-11-02T12:00 and YEAR-11-01T12:00 is always 24
|
|
hours, even though in the US/Eastern timezone, for example, there are
|
|
25 hours between 2014-11-01T12:00 and 2014-11-02T12:00 because the
|
|
local clocks were rolled back one hour at 2014-11-02T02:00,
|
|
introducing an extra hour in the night between 2014-11-01 and
|
|
2014-11-02.
|
|
|
|
Many business applications require the use of Python's simplified view
|
|
of local dates. No self-respecting car rental company will charge its
|
|
customers more for a week that straddles the end of DST than for any
|
|
other week or require that they return the car an hour early.
|
|
Therefore, changing the current rules for aware datetime arithmetics
|
|
will not only create a backward compatibility nightmare, it will
|
|
eliminate support for legitimate and common use cases.
|
|
|
|
Since it is impossible to choose universal rules for local time
|
|
arithmetics, we propose to delegate implementation of those rules to
|
|
the classes that implement ``datetime.tzinfo`` interface. With such
|
|
delegation in place, users will be able to choose between different
|
|
arithmetics by simply picking instances of different classes for the
|
|
value of ``tzinfo``.
|
|
|
|
|
|
Protocol
|
|
========
|
|
|
|
Subtraction of datetime
|
|
-----------------------
|
|
|
|
A ``tzinfo`` subclass supporting the PDDM, may define a method called
|
|
``__datetime_diff__`` that should take two ``datetime.datetime``
|
|
instances and return a ``datetime.timedelta`` instance representing
|
|
the time elapsed from the time represented by the first datetime
|
|
instance to another.
|
|
|
|
|
|
Addition
|
|
--------
|
|
|
|
A ``tzinfo`` subclass supporting the PDDM, may define a method called
|
|
``__datetime_add__`` that should take two arguments--a datetime and a
|
|
timedelta instances--and return a datetime instance.
|
|
|
|
|
|
Subtraction of timedelta
|
|
------------------------
|
|
|
|
A ``tzinfo`` subclass supporting the PDDM, may define a method called
|
|
``__datetime_sub__`` that should take two arguments--a datetime and a
|
|
timedelta instances--and return a datetime instance.
|
|
|
|
|
|
Formatting
|
|
----------
|
|
|
|
A ``tzinfo`` subclass supporting the PDDM, may define methods called
|
|
``__datetime_isoformat__`` and ``__datetime_strftime__``.
|
|
|
|
The ``__datetime_isoformat__`` method should take a datetime instance
|
|
and an optional separator and produce a string representation of the
|
|
given datetime instance.
|
|
|
|
The ``__datetime_strftime__`` method should take a datetime instance
|
|
and a format string and produce a string representation of the given
|
|
datetime instance formatted according to the given format.
|
|
|
|
|
|
Parsing
|
|
-------
|
|
|
|
A ``tzinfo`` subclass supporting the PDDM, may define a class method
|
|
called ``__datetime_strptime__`` and register the "canonical" names of
|
|
the timezones that it implements with a registry. **TODO** Describe a
|
|
registry.
|
|
|
|
|
|
Changes to datetime methods
|
|
===========================
|
|
|
|
Subtraction
|
|
-----------
|
|
|
|
::
|
|
|
|
class datetime:
|
|
def __sub__(self, other):
|
|
if isinstance(other, datetime):
|
|
try:
|
|
self_diff = self.tzinfo.__datetime_diff__
|
|
except AttributeError:
|
|
self_diff = None
|
|
try:
|
|
other_diff = self.tzinfo.__datetime_diff__
|
|
except AttributeError:
|
|
other_diff = None
|
|
if self_diff is not None:
|
|
if self_diff is not other_diff and self_diff.__func__ is not other_diff.__func__:
|
|
raise ValueError("Cannot find difference of two datetimes with "
|
|
"different tzinfo.__datetime_diff__ implementations.")
|
|
return self_diff(self, other)
|
|
elif isinstance(other, timedelta):
|
|
try:
|
|
sub = self.tzinfo.__datetime_sub__
|
|
except AttributeError:
|
|
pass
|
|
else:
|
|
return sub(self, other)
|
|
return self + -other
|
|
else:
|
|
return NotImplemented
|
|
# current implementation
|
|
|
|
|
|
Addition
|
|
--------
|
|
|
|
Addition of a timedelta to a datetime instance will be delegated to the
|
|
``self.tzinfo.__datetime_add__`` method whenever it is defined.
|
|
|
|
|
|
Strict arithmetics
|
|
==================
|
|
|
|
A new abstract subclass of ``datetime.tzinfo`` class called ``datetime.tzstrict``
|
|
will be added to the ``datetime`` module. This subclass will not implement the
|
|
``utcoffset()``, ``tzname()`` or ``dst()`` methods, but will implement some of the
|
|
methods of the PDDM.
|
|
|
|
The PDDM methods implemented by ``tzstrict`` will be equivalent to the following::
|
|
|
|
class tzstrict(tzinfo):
|
|
def __datetime_diff__(self, dt1, dt2):
|
|
utc_dt1 = dt1.astimezone(timezone.utc)
|
|
utc_dt2 = dt2.astimezone(timezone.utc)
|
|
return utc_dt2 - utc_dt1
|
|
|
|
def __datetime_add__(self, dt, delta):
|
|
utc_dt = dt.astimezone(timezone.utc)
|
|
return (utc_dt + delta).astimezone(self)
|
|
|
|
def __datetime_sub__(self, dt, delta):
|
|
utc_dt = dt.astimezone(timezone.utc)
|
|
return (utc_dt - delta).astimezone(self)
|
|
|
|
|
|
Parsing and formatting
|
|
----------------------
|
|
|
|
Datetime methods ``strftime`` and ``isoformat`` will delegate to the namesake
|
|
methods of their ``tzinfo`` members whenever those methods are defined.
|
|
|
|
When the ``datetime.strptime`` method is given a format string that
|
|
contains a ``%Z`` instruction, it will lookup the ``tzinfo``
|
|
implementation in the registry by the given timezone name and call its
|
|
``__datetime_strptime__`` method.
|
|
|
|
Applications
|
|
============
|
|
|
|
This PEP will enable third party implementation of many different
|
|
timekeeping schemes including:
|
|
|
|
* Julian / Microsoft Excel calendar.
|
|
* "Right" timezones with the leap second support.
|
|
* French revolutionary calendar (with a lot of work).
|
|
|
|
Copyright
|
|
=========
|
|
|
|
This document has been placed in the public domain.
|