PEP-0500: A protocol for delegating datetime methods
This commit is contained in:
parent
505278b8c3
commit
983039fed2
|
@ -0,0 +1,204 @@
|
|||
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>
|
||||
Discussions-To: Datetime-SIG <datetime-sig@python.org>
|
||||
Status: Draft
|
||||
Type: Standards Track
|
||||
Content-Type: text/x-rst
|
||||
Requires: 495
|
||||
Created: 08-Aug-2015
|
||||
|
||||
|
||||
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 requre 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 arithmentics
|
||||
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 elapced 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.
|
Loading…
Reference in New Issue