Rename, alias, and deprecate timedelta part attributes

Hi everyone :waving_hand:

I’d like to propose a solution to a problem that I keep running into regularly at work. The names of the attributes of datetime.timedelta objects, .days, .seconds, and .microseconds, are very easily mistaken as representation of the full accumulated value of the timedelta object. However, the only exposed way of accessing that is through the .total_seconds() method. Instead, these attributes rather represents broken apart pieces of a timedelta at different resolutions, that when added together make up the full value.

This issue has surfaced countless of times in code review, and I bet we have lingering production bugs that are due to this, yet to be discovered. This is also backed up by the fact that the .seconds attribute has a special warning call-out in the documentation that informs of this common mixup.

To remedy this issue, I would like to propose a long-term plan of phasing these three attributes out, in preference of a set of new and more verbose names. The attributes would be renamed like below (though feel free to suggest other naming schemes):

  • timedelta.daystimedelta.part_days
  • timedelta.secondstimedelta.part_seconds
  • timedelta.microsecondstimedelta.part_microseconds

The idea of this renaming is that the new names in a much better way are communicating that they are not the full timedelta value, but only a part of it.

In order not to break any existing code, and give plenty of time for users to migrate, the old names will be considered deprecated aliases of the new ones. The implementation could for example be done as @property methods that return the underlying values.

I suggest marking the alias properties with @deprecated("Superseded by .part_$attribute", category=None) for an initial period of 5 years, until all supported Python versions have the new attributes, and then after that with @deprecated("Superseded by .part_$attribute") to give a runtime warning for another 5 years before the existing attributes can be finally scheduled for removal.

Marking the attributes with the non-warning @deprecated() decorator initially will allow IDEs and type checker to yield diagnoses for usages of the attributes, encouraging users to migrate to the newly proposed ones, well ahead of time of their planned removal.

Edit: and also linters can be given functionality for giving diagnoses during this initial period.

I believe this will make Python friendlier to newcomers and long-timers alike, as this is a quirk that’s very easy to miss, a common issue, and not trivial to solve with something like linting, as the attributes also have legitimate uses.

5 Likes

We could also completely get rid of these attributes. They are equivalent to the following expressions:

td.days == td // timedelta(days=1)
td.seconds == (td // timedelta(seconds=1)) % (24 * 60 * 60)
td.microseconds == (td // timedelta(microseconds=1)) % 10**6

I can’t recall a single time when they were needed. Having only days, seconds and microseconds is arbitrary – this is just a leaked implementation detail on 32-bit Python 2. There are no weeks, hours, minutes and milliseconds.

7 Likes

In mxDateTime, which predates the stdlib datetime module, I used these attributes:

To make life easier, the instances also provide a more direct interface to their stored values (these are all read-only):

.day, .hour, .minute, .second

Return the indicated values in their standard ranges. The values are negative for negative time deltas.

.days, .hours, .minutes, .seconds

Return the internal value of the object expressed as float in the resp. units, e.g. TimeDelta(12,00,00).days == 0.5.

(https://www.egenix.com/products/python/mxBase/mxDateTime/doc/#_Toc353894686)

I used the plural forms a lot in my code, since you often need access time deltas in different scales.

The singular forms provided access to the time delta value in broken down form, which were also available via the .tuple() method. You mostly use those when interpreting a time delta as a time value (time of day, as we know it, is simply a time delta against midnight of the current day).

Since the datetime module does have a separate time object, I don’t think the same usage pattern applies and so +1 on deprecating those attributes.

3 Likes

I also find the reasons for deprecation rational.


I’d like to propose that timedelta shall provide a method to export a 5-tuple (days, hours, mins, secs, microsecs). Having one complete set of numbers removes the ambiguity of the current API where there are three separate properties.

Timedelta already has the code to compute these 5 numbers, because it needs them internally in the __str__, which prints DD day(s) HH:MM:SS.ssssss (omiting days and microsecs, if not present). This does not suit everybody, at least because the word “days” is in English or when the fractional seconds are not wanted in the output.

And given the popularity of the SO question “Format timedelta to string” (link) `, there is demand for it.

1 Like

Do you think this would require a PEP if I want to get the ball rolling on this, or would a GitHub issue suffice?

Agreed. There does need to be a way to access broken down values using a new method, if the attributes get deprecated.

2 Likes

I use .days a fairly regularly for things like if delta.days >= 90 checks. I’d be sorry to see it go [1].


  1. even knowing I can do my own .total_seconds() // (60 * 60 * 24) arithmetic ↩︎

1 Like

You can do delta >= timedelta(days=90).

1 Like

A simple issue would be enough.