Permalink
Cannot retrieve contributors at this time
204 lines (155 sloc)
6.9 KB
| 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 <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. |