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