[Python-Dev] Change the repr for datetime.timedelta (was Re: Asynchronous context manager in a typical network server)

Chris Angelico rosuav at gmail.com
Sun Dec 20 20:46:52 EST 2015


On Mon, Dec 21, 2015 at 12:00 PM, Alexander Belopolsky
<alexander.belopolsky at gmail.com> wrote:
> On Sun, Dec 20, 2015 at 5:28 PM, Chris Angelico <rosuav at gmail.com> wrote:
>>
>> > A helpful trivia: a year is approximately π times 10 million seconds.
>>
>> Sadly doesn't help here, as the timedelta for a number of years looks like
>> this:
>>
>> >>> datetime.timedelta(days=365*11)
>> datetime.timedelta(4015)
>>
>
> The original issue was how long is a million seconds.  The bit of trivia
> that I suggested helps to establish that it cannot be a multiple of years.

Ah, true. Still, it would deal with the confusion here, which I think
is what Guido was referring to:

>>> datetime.timedelta(seconds=1000000)
datetime.timedelta(11, 49600)

It's eleven somethings and some loose change. What if it came out like
this, instead?

>>> datetime.timedelta(seconds=1000000)
datetime.timedelta(days=11, seconds=49600)

Much more obviously eleven and a half days.

>> Would there be value in changing the repr to use keyword arguments?
>
>
> I don't think translating from seconds to years will be any simpler with any
> alternative  repr...

A timedelta can't actually cope with years, per se, but for
back-of-the-envelope calculations, 1000 days = 3 years (and round
down).

>>> datetime.timedelta(seconds=1e9)
datetime.timedelta(11574, 6400)

A billion seconds is thirty-odd years. That's about as good as
timedelta's ever going to do for us. Changing the repr won't change
this at all, except that it'll be obvious that the 11K figure is
measured in days.

> but I would really like to see a change in the repr of
> negative timedeltas:
>
>>>> timedelta(minutes=-1)
> datetime.timedelta(-1, 86340)
>
> And str() is not much better:
>
>>>> print(timedelta(minutes=-1))
> -1 day, 23:59:00
>
> The above does not qualify as a human readable representation IMO.

There are two plausible ways of describing negative intervals.

1) Show the largest unit in negative, and all others in positive.

2) Take the absolute value, generate a repr, and then stick a hyphen in front.

If Python picks the former, you can easily request the latter:

ZERO = datetime.timedelta(0)
def display(td):
    if td < 0: return "-"+repr(-td)
    return repr(td)

The converse isn't as easy. And both formats maintain certain
invariants; the second has invariants regarding the magnitude of the
delta (a movement of less than one day will never include the word
'day'), but the first has the rather useful invariant that arithmetic
on datetimes doesn't affect units smaller than those changed:

>>> td = datetime.timedelta(minutes=75)
>>> td + datetime.timedelta(days=1)
datetime.timedelta(1, 4500)
>>> td + datetime.timedelta(days=-1)
datetime.timedelta(-1, 4500)

Also, it's consistent with the way Python handles modulo arithmetic
elsewhere. If you think of a timedelta as a number of microseconds,
the partitioning into days and seconds follows the normal rules for
divmod with 86400 and 1000000.

Yes, it looks a little strange in isolation, but I think it's
justifiable for the .days, .seconds, .microseconds attributes. Should
repr switch the display around? Perhaps, but I'm highly dubious.
Should str? That's a bit more plausible - but since both formats are
justifiable, I'd be more inclined to separate it out; or maybe do this
in __format__ as an alternative formatting style:

>>> class timedelta(datetime.timedelta):
...     def __format__(self, fmt):
...         if fmt == "-" and self.total_seconds() < 0:
...             return "-" + str(-self)
...         return str(self)
...
>>> td=timedelta(minutes=-1)
>>> f"Default: {td}  By magnitude: {td:-}"
'Default: -1 day, 23:59:00  By magnitude: -0:01:00'

ChrisA


More information about the Python-Dev mailing list