|
| 1 | +PEP: 08XX |
| 2 | +Title: Deprecate timedelta part attributes |
| 3 | +Author: Anton Agestam |
| 4 | +Status: Draft |
| 5 | +Type: Standards Track |
| 6 | +Created: 11-Feb-2026 |
| 7 | +Python-Version: 3.15 |
| 8 | + |
| 9 | +Abstract |
| 10 | +======== |
| 11 | + |
| 12 | +This PEP proposes the deprecation and eventual removal of the ``.days``, |
| 13 | +``.seconds``, and ``.microseconds`` attributes from ``datetime.timedelta`` |
| 14 | +objects. These attributes expose broken-down, partial pieces of a time |
| 15 | +difference, and are a frequent source of bugs for users who mistake them |
| 16 | +for unit representations of the total duration. We propose that these attributes |
| 17 | +be phased out entirely. Users should instead rely on direct arithmetic with |
| 18 | +``timedelta`` objects to extract specific components, eliminating the need to |
| 19 | +expose leaked implementation details. |
| 20 | + |
| 21 | + |
| 22 | +Motivation |
| 23 | +========== |
| 24 | + |
| 25 | +A regular problem encountered in Python codebases is the misuse of the |
| 26 | +attributes on ``datetime.timedelta`` objects: ``.days``, ``.seconds``, and |
| 27 | +``.microseconds``. Their naming easily misleads developers into believing |
| 28 | +they represent the full, accumulated value of the object (akin to the |
| 29 | +``.total_seconds()`` method). In reality, they represent broken-apart |
| 30 | +pieces of a timedelta at different resolutions, which only make up the full |
| 31 | +value when added together. |
| 32 | + |
| 33 | +This issue surfaces frequently in code review and causes bugs in production systems. |
| 34 | +The documentation for the ``.seconds`` attribute acknowledges this and |
| 35 | +even contains a special warning call-out regarding this common mix-up. |
| 36 | + |
| 37 | +Exposing exactly days, seconds, and microseconds is |
| 38 | +arbitrary and leaks an implementation detail originating from 32-bit |
| 39 | +Python 2 constraints. There is no consistent API access for other logical |
| 40 | +time units—such as weeks, hours, minutes, or milliseconds. This also hinders |
| 41 | +imaginable evolution of the timedelta type, for instance to support nanosecond |
| 42 | +granularity. |
| 43 | + |
| 44 | + |
| 45 | +Rationale |
| 46 | +========= |
| 47 | + |
| 48 | +Rather than attempting to rename the attributes or introduce new tuple-based |
| 49 | +accessors, removing them entirely offers the most approachable way forward. |
| 50 | +This approach for now avoids the task of designing a new interface for accessing |
| 51 | +parts of a ``timedelta``. The existing arithmetic methods are deemed to work |
| 52 | +well, but this PEP takes no stance on later introducing new methods to the |
| 53 | +``timedelta`` object for improved ergonomics. |
| 54 | + |
| 55 | +The ``datetime`` module already supports floor division between ``timedelta`` |
| 56 | +objects. The exact equivalent values of the current attributes can be |
| 57 | +cleanly and explicitly derived via arithmetic: |
| 58 | + |
| 59 | +* ``td.days`` is equivalent to ``td // timedelta(days=1)`` |
| 60 | +* ``td.seconds`` is equivalent to ``(td // timedelta(seconds=1)) % 86400`` |
| 61 | +* ``td.microseconds`` is equivalent to ``(td // timedelta(microseconds=1)) % 10**6`` |
| 62 | + |
| 63 | +Using timedelta arithmetic inherently protects the user from unit-confusion. |
| 64 | +For example, to check if a delta is greater than 90 days, instead of writing |
| 65 | +``if delta.days >= 90:``, the user can write the unambiguous |
| 66 | +``if delta >= timedelta(days=90):``. |
| 67 | + |
| 68 | +Removing these attributes also frees the internal representation of |
| 69 | +``timedelta`` from its current historical constraints. If precision needs |
| 70 | +to be improved in the future, e.g. adding nanosecond support, users could |
| 71 | +simply divide by ``timedelta(nanoseconds=1)`` without the core developers |
| 72 | +having to design new attributes. |
| 73 | + |
| 74 | + |
| 75 | +Specification |
| 76 | +============= |
| 77 | + |
| 78 | +1. The ``.days``, ``.seconds``, and ``.microseconds`` attributes of |
| 79 | + ``datetime.timedelta`` will be scheduled for deprecation. |
| 80 | + |
| 81 | +2. A long-term transition plan will be enacted: |
| 82 | + * **Phase 1 (Initial 5 years):** The properties will be decorated with |
| 83 | + ``@deprecated`` (e.g. ``@deprecated("Use timedelta arithmetic instead", category=None)``) |
| 84 | + to provide silent warnings that linters, type checkers, and IDEs can |
| 85 | + catch and flag to users well ahead of time. |
| 86 | + * **Phase 2 (Subsequent 5 years):** The deprecation category will be |
| 87 | + upgraded to trigger a runtime ``DeprecationWarning``. |
| 88 | + * **Phase 3:** The attributes can be removed completely, in accordance |
| 89 | + with the backwards compatibility policy outlined in PEP 387. |
| 90 | + |
| 91 | +3. Official documentation will be updated to recommend ``.total_seconds()`` |
| 92 | + for total duration, and arithmetic division with ``timedelta`` objects |
| 93 | + for unit extraction. |
| 94 | + |
| 95 | + |
| 96 | +Backwards Compatibility |
| 97 | +======================= |
| 98 | + |
| 99 | +Because ``timedelta`` is widely used, deprecating these attributes will |
| 100 | +impact existing codebases. To minimize disruption, this PEP stipulates a |
| 101 | +long deprecation period as per PEP 387 (totaling 10 years) before ultimate |
| 102 | +removal. This follows Python's normal deprecation cycle for widely used |
| 103 | +standard library features, ensuring developers have ample time to migrate |
| 104 | +without sudden breakage. |
| 105 | + |
| 106 | + |
| 107 | +Rejected Ideas |
| 108 | +============== |
| 109 | + |
| 110 | +Rename attributes to ``.part_days``, ``.part_seconds``, etc. |
| 111 | +------------------------------------------------------------ |
| 112 | +An `initial proposal <https://discuss.python.org/t/rename-alias-and-deprecate-timedelta-part-attributes/97674/1>`_ |
| 113 | +suggested renaming the attributes to prefix them with ``part_`` to signify |
| 114 | +they are incomplete fragments of the duration. This is rejected as the parts |
| 115 | +themselves are arbitrary, and eliminating them entirely leaves a clean API. It |
| 116 | +is deemed that these properties are not useful enough to warrant being part of |
| 117 | +the API, and in the cases they are needed, they can still be calculated. |
| 118 | + |
| 119 | +Return a tuple of internal values |
| 120 | +--------------------------------- |
| 121 | +Suggestions were made to add a method returning the internally stored |
| 122 | +values as a tuple, such as `(days, seconds, microseconds) <https://discuss.python.org/t/rename-alias-and-deprecate-timedelta-part-attributes/97674/13>`_, |
| 123 | +or a simplified ``(days, seconds)`` where seconds is a float. Others suggested |
| 124 | +exporting a `5-tuple: (days, hours, mins, secs, microsecs) <https://discuss.python.org/t/rename-alias-and-deprecate-timedelta-part-attributes/97674/4>`_. |
| 125 | +This is similarly rejected due to being deemed of little value to users, as well |
| 126 | +as because it exposes an internal implementation detail. |
| 127 | + |
| 128 | +If timedelta precision is later improved to include nanoseconds, the size of the |
| 129 | +tuple would have to change. Furthermore, the order of tuple components does not |
| 130 | +inherently match constructor arguments. The arithmetic approach is safer and |
| 131 | +more flexible. |
| 132 | + |
| 133 | +Keep ``.days`` but deprecate the rest |
| 134 | +------------------------------------- |
| 135 | +Some `argued <https://discuss.python.org/t/rename-alias-and-deprecate-timedelta-part-attributes/97674/21>`_ |
| 136 | +that ``.days`` should be kept because it is unbounded and does not have the |
| 137 | +exact same "footgun" characteristics as ``.seconds``. This is rejected in part |
| 138 | +for the sake of exposing a consistent API, and in part because the days |
| 139 | +attribute has other footguns as `was pointed out in the discussion <https://discuss.python.org/t/rename-alias-and-deprecate-timedelta-part-attributes/97674/22>`_: |
| 140 | +``timedelta(seconds=-1).days == -1``. |
| 141 | + |
| 142 | +Furthermore, the standard use case for ``.days`` |
| 143 | +(e.g. ``if delta.days >= 90``) is `better served <https://discuss.python.org/t/rename-alias-and-deprecate-timedelta-part-attributes/97674/8>`_ |
| 144 | +by unambiguous ``timedelta`` object comparison: ``if delta >= timedelta(days=90)``. |
| 145 | + |
| 146 | +Provide a ``__format__`` method |
| 147 | +------------------------------- |
| 148 | +A `tangential suggestion <https://discuss.python.org/t/rename-alias-and-deprecate-timedelta-part-attributes/97674/10>`_ |
| 149 | +highlighted the difficulty of formatting ``timedelta`` objects into |
| 150 | +human-readable strings, which often requires breaking down the parts, prompting |
| 151 | +a request for a dedicated ``__format__`` method. While formatting is a valid |
| 152 | +concern with high community demand, it is an orthogonal issue to the structural |
| 153 | +representation of the class. This idea is explicitly deferred to future |
| 154 | +independent design improvements of this type and considered out of scope of this |
| 155 | +PEP. |
| 156 | + |
| 157 | +Make microseconds a float part of seconds |
| 158 | +----------------------------------------- |
| 159 | +A `suggestion was raised <https://discuss.python.org/t/rename-alias-and-deprecate-timedelta-part-attributes/97674/12>`_ |
| 160 | +to eliminate the microseconds attribute by rolling it into seconds as a float. |
| 161 | +This was rejected, as it is strictly an implementation detail and does not solve |
| 162 | +the overarching issue of users mistaking partial properties for total accumulated |
| 163 | +durations. |
0 commit comments