[Python-Dev] datetime.timedelta total_microseconds

Chris Barker chris.barker at noaa.gov
Tue Feb 26 19:03:12 EST 2019


This thread petered out, seemingly with a consensus that we should update
the docs -- is anyone doing that?

But anyway, I'd like to offer a counterpoint:

>From the OP, it is clear that:

* Folks have a need for timedeltas to be converted to numbers values, with
units other than seconds (milliseconds, at least).
* If left to their own devices, they may well do it wrong (Or at least not
as right as they should.

So: it would be good to provide a correct, simple,  intuitive, and
discoverable way to do that.

timedelta.total_seconds()

Provides that for seconds, but there is no equivalent for other units.

a_time_delta / timedelta(microseconds=1)

Is now possible in py3, and has been proposed as the canonical way to
convert to specific time units.

However, while it does provide a correct[1] way to do it, it is:

- not very simple
- not very intuitive.
- not the least bit discoverable

Each of these in turn:

simple:
=====

compare

duration = a_timedelta.total_seconds()

to

duration = a_timedelta / datetime.timedelta(seconds=1)

Keep in mind that the timedelta object may have been generated by another
module somehow, so the coder that gets it and wants to turn it into a
number of seconds (or milliseconds, or ...) needs to import datetime and
reference the timedelta object. And if they are converting to a plain
number, it's probably because they don't want to be working with timedeltas
at all anyway. So no, not so simple.

intuitive:
======

A casual user reading the first will very likely know what it means -- a
casual user reading the second line will need to think about it carefully,
and probably have to go read the datetime docs, or at least do some
experiments to make sure it does what they think it does. Granted, a
comment:

duration = a_timedelta / datetime.timedelta(seconds=1)  # convert to seconds

would help a lot, but if you need a comment to explain a line of code this
simple, then it's not intuitive.

A couple more data points:

-- I am a physical scientist, I work with unitted quantities all the time
(both in code and in other contexts). It never dawned on me to use this
approach to convert to seconds or milliseconds, or ... Granted, I still
rely on python2 for a fair bit of my work, but still, I had to scratch my
head when it was proposed on this thread.

-- There are a number of physical unit libraries in Python, and as far as I
know, none of them let you do this to create a unitless value in a
particular unit. "pint" for example:

https://pint.readthedocs.io/en/latest/

In pint, you can create objects with units, including time:

In [50]: timespan = 2 * ureg.day

In [51]: print(timespan)

2 day

But if you divide a value of days by a value in seconds, you don't get a
unitless seconds per day:

In [54]: unitless = timespan / (1 * ureg.second)

In [55]: print(unitless)

2.0 day / second

Though pint does know it is dimensionless:

In [56]: unitless.dimensionless

Out[56]: True

And you can reduce it to a dimensionless object:

In [57]: unitless.to_reduced_units()

Out[57]: 172800.0 <Unit('dimensionless')>

And there is your seconds value.

But the "right" way to get a given pint object of time into particular
units is to convert, and then, if you want a plain number, get the
magnitude:

In [53]: print(timespan.to('second').magnitude)

172800.0

So no -- dividing a datetime by another datetime with the value you want is
not intuitive: not to a physical scientist, not to a user of other physical
quantities libraries -- is it intuitive to anyone other than someone that
was involved in python datetime development??

Discoverable:
==========

It is clearly not discoverable -- the OP didn't find it, and no one other
than Alexander found it on this thread (I'm pretty sure). That could be
made much better with docs, but we all know no one reads docs -- I'm
willing to bet that even if we better document it, folks will still be
writing utility functions like the OP posted.

And  (this is also a doc issue) -- I wanted to know what the options were
for units we could specify to the datetime constructor, so I used the nifty
iPython ?, and got:

In [59]: timedelta?

Init signature: timedelta(self, /, *args, **kwargs)
Docstring:      Difference between two datetime values.


Gosh, that's helpful! (we really need to fix that regardless of this
thread). And someone earlier mentioned "weeks" without realizing that is
was already supported:

In [60]: timedelta(weeks=1)

Out[60]: datetime.timedelta(days=7)

So we have a real discoverability problem -- we really need to fix that.

On the other hand, if we add a few convenience methods, we will have a way
for folks to do this that is:

correct
simple
intuitive
discoverable

And we really don't need to add many. Looking at the docs ('cause the
docstring is useless), I see that the timedelta takes:

datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0,
minutes=0, hours=0, weeks=0)

So at most, we could have:

.total_microseconds()
.total_seconds()
.total_minutes()
.total_hours()
.total_days()
.total_weeks()

Yes, I know that that's more code to write, maintain and document, but
really, it will not take long to write, will take less work to document
than the doc improvements we need anyway, and hardly any extra maintenance
burden.

(and the utility of weeks and minutes is questionable)

BTW, why are these methods, rather than properties?

Another option, is, of course, to add a:

.to_unit() method that takes the above as strings. -- but unless we ar
going to support a bunch more units than those six, I'd rather see the
methods. And what would those others be? fortnights?

I've made my case -- and maybe we won't do this. But let's please at least
update the docstring of timedleta!

-CHB


[1] is it the best possible for microseconds? it returns a float, which can
only carry 15 or so digits, which is "only" 300 or so years. So microsecond
precision is lost for timedeltas representing longer than that -- does that
matter ???


-- 

Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R            (206) 526-6959   voice
7600 Sand Point Way NE   (206) 526-6329   fax
Seattle, WA  98115       (206) 526-6317   main reception

Chris.Barker at noaa.gov
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20190226/f9b7d90e/attachment.html>


More information about the Python-Dev mailing list