classification
Title: Allow using decimals as arguments to `timedelta`
Type: enhancement Stage:
Components: Library (Lib) Versions: Python 3.4
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Alexander.Belopolsky, belopolsky, cool-RR, facundobatista, mark.dickinson, rhettinger, vstinner
Priority: normal Keywords: patch

Created on 2012-03-12 13:13 by cool-RR, last changed 2014-10-14 15:00 by skrah.

Files
File name Uploaded Description Edit
timedelta_decimal.patch vstinner, 2012-03-13 12:51 review
Messages (22)
msg155449 - (view) Author: Ram Rachum (cool-RR) * Date: 2012-03-12 13:13
Please allow using decimals as arguments to `timedelta`, so the following code won't raise an exception:

Python 3.3.0a1 (default, Mar  4 2012, 17:27:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> import decimal
>>> datetime.timedelta(hours=decimal.Decimal(7))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported type for timedelta hours component: Decimal

It's really annoying to have to convert all the arguments to `float` every time I instantiate a `timedelta`.
msg155583 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2012-03-13 09:11
The PEP 410 was rejected. See also the issue #13882.
msg155587 - (view) Author: Ram Rachum (cool-RR) * Date: 2012-03-13 10:16
I'm not proposing that `timedelta` will use `Decimal` internally, but that it would handle the conversion to `float` itself, instead of the user having to do it.
msg155602 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2012-03-13 12:51
Attached patch changes timedelta constructor to accept decimal.Decimal.

See also the issue #14180.
msg155609 - (view) Author: Ram Rachum (cool-RR) * Date: 2012-03-13 13:37
Thanks for the patch!
msg155627 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2012-03-13 16:11
I am -0 on the feature and -1 on the implementation.  Conversion from Decimal to float is explicit by design.  Decimal gives the user fine control over rounding issues allowing for either exact arithmetics (trapping inexact operation) or one of several rounding modes.

This said, timedelta(<decimal>) is not much worse than float(<decimal>), so I will be only -0 if the implementation is such that (1) timedelta(<decimal>) does no loose precision over the entire range of timedelta and rounding is documented; and (2) implementaton does not require an explicit import decimal inside the datetime  module.
msg156371 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2012-03-20 00:14
My patch looses precision for big numbers. It's better to convert Decimal to a number of microseconds.

Wrong:

>>> value=86400*365.25*999999+1e-6; print(datetime.timedelta(seconds=value))
365249634 days, 18:00:00
>>> value=decimal.Decimal(86400*365.25*999999)+decimal.Decimal('1e-6'); print(datetime.timedelta(seconds=float(value)))
365249634 days, 18:00:00

Correct:

>>> value=decimal.Decimal(86400*365.25*999999)+decimal.Decimal('1e-6'); print(datetime.timedelta(microseconds=int(value*decimal.Decimal(10**6))))
365249634 days, 18:00:00.000001

I'm not completly conviced by the need of supporting Decimal in timedelta constructor. Why do you use Decimal if the result should be a timedelta? Why not using timedelta directly?
msg156393 - (view) Author: Ram Rachum (cool-RR) * Date: 2012-03-20 10:18
"I'm not completly conviced by the need of supporting Decimal in timedelta constructor. Why do you use Decimal if the result should be a timedelta? Why not using timedelta directly?"

What do you mean, "Why not using timedelta directly?" How could I use timedelta directly in the timedelta constructor? I'm creating a timedelta, I don't have one ready.

I'm getting an `n_hours` variable for some component of my system. And this value happens to come as a `Decimal`. I want to create a `timedelta` out of it. So I'd want to be able to do `timedelta(hours=n_hours)` rather than `timedelta(hours=float(n_hours))`.
msg156396 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2012-03-20 11:35
"I'm getting an `n_hours` variable for some component of my system.
And this value happens to come as a `Decimal`."

Can't you modify your program to use timedelta instead of Decimal for n_hours?
msg156398 - (view) Author: Ram Rachum (cool-RR) * Date: 2012-03-20 11:39
In some cases we indeed use a timedelta directly, but sometimes we get a "number of hours", and in those cases it's more convenient for us to work with "number of hours" directly.
msg170746 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012-09-19 18:56
> (1) timedelta(<decimal>) does no loose precision over the entire range
> of timedelta and rounding is documented;

Agreed that this should be a requirement.
msg170748 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2012-09-19 19:04
Mark wrote in his comment on issue 15975:

> we're looking at significant extra code to implement
> Decimal * timedelta

Not necessarily.  I will only support adding this feature if it can be done without making datetime know about Decimal.  If we can agree on a lossless protocol to communicate floating pointing numbers of different base (or even more generally rational numbers), datetime module can support this protocol and allow all kinds of numbers in its constructors without much extra code.
msg170749 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012-09-19 19:05
@Ram Rachum:  out of curiosity, where are your Decimal objects coming from in the first place?
msg170750 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2012-09-19 19:07
> If we can agree on a lossless protocol to communicate floating pointing
> numbers of different base

What sort of thing did you have in mind?  This is sounding like a PEP-level proposal.
msg170751 - (view) Author: Ram Rachum (cool-RR) * Date: 2012-09-19 19:09
@mark.dickinson: Many different sources. One example is decimal fields on Django, used for dollar amounts.
msg170753 - (view) Author: Alexander Belopolsky (Alexander.Belopolsky) Date: 2012-09-19 19:20
On Wed, Sep 19, 2012 at 3:09 PM, Ram Rachum <report@bugs.python.org> wrote:
> One example is decimal fields on Django, used for dollar amounts.

.. and since time is money and money is time we should support easy
conversion between the two. :-)
msg170754 - (view) Author: Ram Rachum (cool-RR) * Date: 2012-09-19 19:24
I hope this was intended as a joke. If this was an actual criticism, let me know so I could explain why it makes sense.
msg170758 - (view) Author: Alexander Belopolsky (Alexander.Belopolsky) Date: 2012-09-19 19:34
On Wed, Sep 19, 2012 at 3:24 PM, Ram Rachum <report@bugs.python.org> wrote:
> I hope this was intended as a joke. If this was an actual criticism, let me know so
> I could explain why it makes sense.

It was both.  Yes, any use cases will be helpful.  Timedelta is
already a decimal with six fractional digits, so it is natural to
desire having direct Decimal to timedelta conversion, but without
important use-case this feature will be hard to sell.
msg170769 - (view) Author: Ram Rachum (cool-RR) * Date: 2012-09-19 20:32
I can think of millions of use cases. Here's a random one out of those millions: A client is entitled to X hours of service a month. We grant him a promotion where he is allowed 15% more than x, i.e. 1.15*x. But that number, 1.15, is stored in a Django decimal field. We try to multiply 1.15 by the timedelta X and it fails.
msg170771 - (view) Author: Alexander Belopolsky (Alexander.Belopolsky) Date: 2012-09-19 20:57
On Wed, Sep 19, 2012 at 4:32 PM, Ram Rachum <report@bugs.python.org> wrote:
> But that number, 1.15, is stored in a Django decimal field.

My criticism was towards the idea that one may need to multiply
timedelta by a dollar amount or convert a dollar amount to a
timedelta.   Storing dollar amounts is an important use case for
Decimal and there are several reasons why Decimal is a better choice
than float for this.  On the other hand, I don't see why use of
Decimal would be required in your example.

Do you have any real world use cases where Decimal is clearly
preferred over float and there is a need to multiply Decimals by time
deltas?
msg170773 - (view) Author: Ram Rachum (cool-RR) * Date: 2012-09-19 21:36
In the example I gave, Decimal is clearly preferred over float. Why would we use float to represent the ratio of the bonus to the client? Why would we risk imprecision there when Decimal provides us with perfect precision?
msg170776 - (view) Author: Alexander Belopolsky (Alexander.Belopolsky) Date: 2012-09-19 22:38
On Sep 19, 2012, at 5:36 PM, Ram Rachum <report@bugs.python.org> wrote:

> Why would we use float to represent the ratio of the bonus to the client?

Because float is the builtin type that Python provides to represent such quantities. 

> Why would we risk imprecision there when Decimal provides us with perfect precision?

Python float is a much simpler and more efficient type than Decimal.  One should have a really good reason to introduce Decimal in the program.  In case of money, Decimal provides a *lower* precision alternative to float together with the control over rounding direction.  This is important in applications where fractions of a penny have to be dealt with in very precise manner. 

In your application, float is a perfectly good type to represent 15% bonus.  Even if your customers insist on microsecond precision, float is good enough and timedelta resolution will prevent you from supporting higher precision anyways. 

As I mentioned before, I would be happy to see greater interoperability between numerical types in Python and interoperability between timedelta and Decimal may come as a side benefit of that effort.  However, I don't find your use case to be compelling enough to either justify special case code or to motivate a more general effort.
History
Date User Action Args
2014-10-14 15:00:08skrahsetnosy: - skrah
2012-09-19 22:38:02Alexander.Belopolskysetmessages: + msg170776
2012-09-19 21:36:56cool-RRsetmessages: + msg170773
2012-09-19 20:57:20Alexander.Belopolskysetmessages: + msg170771
2012-09-19 20:32:06cool-RRsetmessages: + msg170769
2012-09-19 19:34:10Alexander.Belopolskysetmessages: + msg170758
2012-09-19 19:24:43cool-RRsetmessages: + msg170754
2012-09-19 19:20:18Alexander.Belopolskysetnosy: + Alexander.Belopolsky
messages: + msg170753
2012-09-19 19:09:43cool-RRsetmessages: + msg170751
2012-09-19 19:07:38mark.dickinsonsetmessages: + msg170750
2012-09-19 19:05:21mark.dickinsonsetmessages: + msg170749
2012-09-19 19:04:36belopolskysetmessages: + msg170748
2012-09-19 18:58:00mark.dickinsonlinkissue15975 superseder
2012-09-19 18:56:53mark.dickinsonsetmessages: + msg170746
2012-09-19 18:55:28mark.dickinsonsetversions: + Python 3.4, - Python 3.3
2012-03-20 11:39:58cool-RRsetmessages: + msg156398
2012-03-20 11:35:48vstinnersetmessages: + msg156396
2012-03-20 10:18:02cool-RRsetmessages: + msg156393
2012-03-20 00:14:54vstinnersetmessages: + msg156371
2012-03-20 00:03:09vstinnersetnosy: + skrah
2012-03-13 16:11:30belopolskysettype: enhancement
messages: + msg155627
2012-03-13 13:37:25cool-RRsetmessages: + msg155609
2012-03-13 12:51:48vstinnersetfiles: + timedelta_decimal.patch
keywords: + patch
messages: + msg155602
2012-03-13 10:16:01cool-RRsetmessages: + msg155587
2012-03-13 09:11:04vstinnersetmessages: + msg155583
2012-03-13 02:32:01eric.araujosetnosy: + rhettinger, facundobatista, mark.dickinson, belopolsky, vstinner
2012-03-12 13:13:07cool-RRcreate