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.