This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: future division breaks timedelta division by integer
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 2.7
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: Nosy List: belopolsky, lemburg, madison.may, mark.dickinson, pitrou, tim.peters
Priority: normal Keywords:

Created on 2013-08-02 15:22 by exarkun, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Messages (18)
msg194181 - (view) Author: Jean-Paul Calderone (exarkun) * (Python committer) Date: 2013-08-02 15:22
datetime.timedelta instances are divisible by integers on Python 2.7, but not when __future__.division has been turned on:


exarkun@top:~$ ~/Projects/cpython/2.7/python -c '
from datetime import timedelta
print timedelta(seconds=3) / 2
'
0:00:01.500000
exarkun@top:~$ ~/Projects/cpython/2.7/python -c '
from __future__ import division
from datetime import timedelta
print timedelta(seconds=3) / 2
'
Traceback (most recent call last):
  File "<string>", line 4, in <module>
TypeError: unsupported operand type(s) for /: 'datetime.timedelta' and 'int'
exarkun@top:~$ ~/Projects/cpython/2.7/python
Python 2.7.5+ (2.7:8205e72b5cfc, Aug  2 2013, 11:12:04) 

This presents a minor barrier to Python 3 transitions, since it prevents the use of __future__.division in a module trying to retain Python 2 compatibility where timedelta division is used.
msg194183 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2013-08-02 16:21
You're just looking for floor division:

$ python -Qnew
Python 2.7.4 (default, Apr 19 2013, 18:28:01) 
[GCC 4.7.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 1/2
0.5
>>> from datetime import timedelta
>>> print timedelta(seconds=3) // 2
0:00:01.500000
msg194186 - (view) Author: Jean-Paul Calderone (exarkun) * (Python committer) Date: 2013-08-02 17:05
Hm.  Maybe I am.  Yet isn't true division implemented for this pair of types in Python 3?  I'm not sure why it shouldn't be implemented for them in Python 2.

Also that raises another question.  Does a result of one and one half seconds make sense as the result of a floor division operation?
msg194207 - (view) Author: Madison May (madison.may) * Date: 2013-08-02 21:27
I agree -- it's not at all intuitive that the floor division returns a decimal number of seconds, while float division raises an error.  This should probably be cleaned up.   

In python 3.4, both float division and integer division return decimal numbers of seconds -- that's not all that intuitive, either.  

Python 3.4.0a0 (default:d5536c06a082+, Jul 11 2013, 20:23:54) 
[GCC 4.8.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import timedelta
>>> print(timedelta(seconds=3)/2)
0:00:01.500000
>>> print(timedelta(seconds=3)//2)
0:00:01.500000
msg194216 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2013-08-02 22:43
I believe this is related to the fact that timedelta * float is not supported in 2.x:

Python 2.7.5 (default, May 24 2013, 15:56:16)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import *
>>> timedelta(2) * 0.5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for *: 'datetime.timedelta' and 'float'

I don't see why this is a barrier to Python 3 transitions.  Before you turn on true division you should replace all instances of floor division with '//' anyways.  Just treat instances of timedelta / int as you would instances of int / int.
msg194217 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2013-08-02 22:47
> Does a result of one and one half seconds make sense as the result of a floor division operation?

Yes.  Timedeltas behave as integers containing the number of microseconds:

>>> timedelta(microseconds=1) / 2
datetime.timedelta(0)
msg194246 - (view) Author: Marc-Andre Lemburg (lemburg) * (Python committer) Date: 2013-08-03 10:48
On 03.08.2013 00:47, Alexander Belopolsky wrote:
> 
> Alexander Belopolsky added the comment:
> 
>> Does a result of one and one half seconds make sense as the result of a floor division operation?
> 
> Yes.  Timedeltas behave as integers containing the number of microseconds:
> 
>>>> timedelta(microseconds=1) / 2
> datetime.timedelta(0)

I think that's a very obscure interpretation of floor division for
timedeltas :-)

IMO, floor division result should work on seconds, not microseconds.
msg194247 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2013-08-03 11:03
> I think that's a very obscure interpretation of floor division for
> timedeltas :-)

Agreed.
msg194271 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2013-08-03 16:32
What is so special about seconds?  Why not days? As in

>>> timedelta(3) // 2
timedelta(1)


Note that in 3.x we have timedelta over timedelta division that lets you do floor division in arbitrary time units.

What is the use case for timedelta // int that rounds down to a second?  I suspect in most cases you really want timedelta // timedelta(seconds=int) instead.
msg194272 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2013-08-03 16:36
I'm not sure I see a use-case for timedelta // int at all.  To make sense of that, you first need some way to make sense of floor(timedelta), and as you say it's not clear what that should mean:  number of seconds?  number of days?  Either of those would seem more natural than number of microseconds, though.

timedelta // timedelta and timedelta / int on the other hand have an obvious meaning.
msg194273 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2013-08-03 16:39
-1 on changing the behaviour in 2.7, though;  I think it's far too late for that.
msg194274 - (view) Author: Marc-Andre Lemburg (lemburg) * (Python committer) Date: 2013-08-03 16:43
On 03.08.2013 18:32, Alexander Belopolsky wrote:
> 
> Alexander Belopolsky added the comment:
> 
> What is so special about seconds?  Why not days? As in
> 
>>>> timedelta(3) // 2
> timedelta(1)
> 
> 
> Note that in 3.x we have timedelta over timedelta division that lets you do floor division in arbitrary time units.
> 
> What is the use case for timedelta // int that rounds down to a second?  I suspect in most cases you really want timedelta // timedelta(seconds=int) instead.

The notion of fraction in time usually applies to seconds, not
days, hours or minutes. Since floor removes fractions, the natural
expectation is to have // int apply to seconds, not microseconds
(which represent fractions of a second).

That said, I don't think having // division on timedeltas is useful
at all. I'd be +1 on removing this support and raise a TypeError
instead.
msg194277 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2013-08-03 17:44
Well, a timedelta is a duration.  timedelta // n is as close as possible to one n'th of that duration, but "rounding down" (if necessary) so that the result is representable as a timedelta.  In the same way, if i and j are integers, i // j is as close as possible to one j'th of i, but "rounding down" (if necessary) so that the result is representable as an integer.  Like:

>>> from datetime import timedelta
>>> timedelta(1) // 7
datetime.timedelta(0, 12342, 857142)
>>> timedelta(1) - 7 * _
datetime.timedelta(0, 0, 6)
>>>

The last line shows the part truncated away:  one seventh of a day is not representable as a timedetla. If `timedelta // int` rounded to the closest representable timedelta, it would return timedelta(0, 12342, 857143) instead.  That's a little bigger than a seventh of a day:

>>> timedelta(0, 12342, 857143) * 7
datetime.timedelta(1, 0, 1)

It has nothing directly to do with days, hours, seconds ... it so happens that timedelta has microsecond resolution, so the closest representable approximations to one n'th of a timedelta most often have non-zero microsecond components.  If timedelta had femtosecond resolution, they'd most often have non-zero femtosecond components ;-)
msg194278 - (view) Author: Jean-Paul Calderone (exarkun) * (Python committer) Date: 2013-08-03 17:50
> I think that's a very obscure interpretation of floor division for
timedeltas :-)

Note - I don't care about this.  I just want `timedelta / int` to do the same thing in Python 2.7 with __future__.division as `timedelta / int` does in Python 3.

Please don't reject this because of some unrelated discussion about how floor division should be implemented for timedelta.
msg194281 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2013-08-03 18:19
There are two schools of thought here.  One school (MAL and Mark) thinks of durations as real number of seconds.  The other school (Tim and I) think of durations as integer number of resolution intervals.  This is why I and Tim before me resisted adding true division of timedelta by a number and multiplication of timedelta by a float.  Mark prevailed on that issue (see #1289118.)  His expertise in floating point computing certainly helped designing and implementing these operations correctly.  We still have a small quirk waiting to be ironed out (see #8860), but overall I am happy with the current state.

This said, the datetime module carries a long legacy of being a pure integer arithmetics in a funny variable-radix notation.  We can disagree on the utility of floor division as it is currently defined, but we cannot remove or change it in an incompatible way.
msg194287 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2013-08-03 18:35
> I just want `timedelta / int` to do the same thing in Python 2.7
> with __future__.division as `timedelta / int` does in Python 3.

It other words you want to backport timedelta / int true division.  I am afraid it is 3-4 years too late for this request, but I will let others make a call.  Count me as -0.

I usually prefer explicit error over an obscure bug.  If we backport true division the -Qnew flag will be changing the meaning of timedelta / int very slightly: instead of rounding down it will round to nearest.  There are cases where this difference is important.  Furthemore, when people compare timedeltas in tests they rarely do it with precision tolerance.  If you have a good test coverage change from floor to true division may break your tests.

My recommendation is: replace timedelta / int with timedelta // int in your 2.x code.
msg194303 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2013-08-03 21:23
This issue is effectively a duplicate #1083 (see msg101281.)
msg227424 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2014-09-24 08:56
It's not a duplicate.
History
Date User Action Args
2022-04-11 14:57:48adminsetgithub: 62829
2015-08-28 21:25:03belopolskysetstatus: open -> closed
resolution: wont fix
stage: resolved
2015-04-03 00:22:30Jean-Paul Calderonesetnosy: - exarkun
2014-09-24 08:56:05pitrousetstatus: pending -> open

messages: + msg227424
2014-09-22 18:41:43serhiy.storchakasetstatus: open -> pending
2013-08-03 21:23:28belopolskysetmessages: + msg194303
2013-08-03 18:35:51belopolskysetmessages: + msg194287
2013-08-03 18:19:22belopolskysetmessages: + msg194281
2013-08-03 17:50:54exarkunsetmessages: + msg194278
2013-08-03 17:44:39tim.peterssetnosy: + tim.peters
messages: + msg194277
2013-08-03 16:43:50lemburgsetmessages: + msg194274
2013-08-03 16:39:45mark.dickinsonsetmessages: + msg194273
2013-08-03 16:36:18mark.dickinsonsetmessages: + msg194272
2013-08-03 16:32:10belopolskysetmessages: + msg194271
2013-08-03 11:03:16mark.dickinsonsetnosy: + mark.dickinson
messages: + msg194247
2013-08-03 10:48:08lemburgsetmessages: + msg194246
2013-08-02 22:47:52belopolskysetmessages: + msg194217
2013-08-02 22:43:31belopolskysetmessages: + msg194216
2013-08-02 21:33:12pitrousetnosy: + lemburg, belopolsky
2013-08-02 21:27:58madison.maysetnosy: + madison.may
messages: + msg194207
2013-08-02 17:05:15exarkunsetmessages: + msg194186
2013-08-02 16:21:16pitrousetnosy: + pitrou
messages: + msg194183
2013-08-02 15:22:11exarkuncreate