PEP 500 – A protocol for delegating datetime methods to their tzinfo implementations
- Author:
- Alexander Belopolsky <alexander.belopolsky at gmail.com>, Tim Peters <tim.peters at gmail.com>
- Discussions-To:
- Datetime-SIG list
- Status:
- Rejected
- Type:
- Standards Track
- Requires:
- 495
- Created:
- 08-Aug-2015
- Resolution:
- Datetime-SIG message
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.
Source: https://github.com/python/peps/blob/main/peps/pep-0500.rst
Last modified: 2023-09-09 17:39:29 GMT