Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InexactError when dividing or multiplying Dates.Millisecond #15322

Open
IanButterworth opened this issue Mar 2, 2016 · 12 comments
Open

InexactError when dividing or multiplying Dates.Millisecond #15322

IanButterworth opened this issue Mar 2, 2016 · 12 comments
Labels
dates Dates, times, and the Dates stdlib module docs This change adds or pertains to documentation

Comments

@IanButterworth
Copy link
Sponsor Member

Normal behaviour with second precision:

x_min = DateTime("2016-02-22T16:14:39")
x_max = DateTime("2016-02-22T20:23:51")
x_span = x_max-x_min
println(x_span)
y = x_span/20.0
println(y)

14952000 milliseconds
747600 milliseconds

Behaviour if date times have sub-second precision:

x_min = DateTime("2016-02-22T15:38:45.234")
x_max = DateTime("2016-02-22T19:27:35.363")
x_span = x_max-x_min
println(x_span)
y = x_span/20.0
println(y)

13730129 milliseconds
LoadError: InexactError()
while loading In[51], in expression starting on line 7

in / at dates/periods.jl:49

@tkelman tkelman added the dates Dates, times, and the Dates stdlib module label Mar 2, 2016
@pkofod
Copy link
Contributor

pkofod commented Mar 2, 2016

If you look here https://github.com/JuliaLang/julia/blob/master/base/dates/types.jl#L15-L20
You will see that hours, minutes, seconds, and milliseconds are Int64s

julia> x_min = DateTime("2016-02-22T15:38:45.230")
2016-02-22T15:38:45.23

julia> x_max = DateTime("2016-02-22T19:27:35.360")
2016-02-22T19:27:35.36

julia> x_max-x_min
13730130 milliseconds

julia> (x_max-x_min)/15
915342 milliseconds

julia> (x_max-x_min)/16
ERROR: InexactError()
 in /(::Base.Dates.Millisecond, ::Int64) at ./dates/periods.jl:57
 in eval(::Module, ::Any) at ./boot.jl:267

So if (x_max-x_min)/y) also is to be a Millisecond type, then it will have to return something that can be represented by an Int64, but then you have to use div

div((x_max-x_min),16)
858133 milliseconds

or (if you really want the decimal number)

(x_max-x_min).value/16
858133.125

@nalimilan
Copy link
Member

Indeed, your first example only works because the division of 14952000 by 20 is exact, so the floating point result is converted without loss to an integer.

Note that instead of div, you can use the corresponding operator ÷ (\div + [tab]).

I understand this can be annoying, especially for newcomers, but Julia usually doesn't do automatic rounding. @quinnj?

@IanButterworth
Copy link
Sponsor Member Author

I see! Thanks for clarifying.
I did expect rounding in these functions. I'll keep an eye out in the future if non-rounding is the norm for Julia.

@nalimilan
Copy link
Member

That said, it doesn't make much sense to allow using / given that it will fail most of the time. If we don't want to round automatically, we should at least raise an error and tell the user to use ÷. That would avoid this surprising behavior where it works for some values and not for others.

The same problem happens with non-integer multiplication. In that case, we don't have an equivalent of div, so one needs to use round.

@nalimilan nalimilan changed the title Inexact error in dates/periods.jl when working with DateTimes with sub-second precision InexactError when dividing or multiplying Dates.Millisecond Mar 2, 2016
@vtjnash vtjnash added the docs This change adds or pertains to documentation label Mar 8, 2016
@amellnik
Copy link
Contributor

This is the reason why I often have to convert DateTimes to unixtime before plotting with a package like Gadfly, since things like boxplots or density plots require / to behave nicely. I'm not sure what a better solution looks like, but DateTimes are fairly painful to work with at the moment.

@nalimilan
Copy link
Member

Boxplots need to compute the mean so I understand why they fail currently, but why would a density plot fail without /?

@ajkeller34
Copy link
Contributor

I just ran into this problem myself when trying to rescale an interval of time by a Float64 (e.g. then = now(); sleep(0.1); (now() - then)/0.1). As has been said in this thread, there's no automatic rounding in Julia, but with ordinary numbers there is automatic promotion, which sets up an expectation for this sort of operation to work. There may be compelling reasons to have Millisecond be integer-based but this doesn't seem like an operation that should result in an error.

@hmmueller
Copy link

hmmueller commented Jul 31, 2019

This is unexpected - I expected finding the mean between two dates as (b-a)/2 ... and that actually works almost always, until one day ...

The following code

for i in range(1,length=10)
  println((DateTime(2000,2,1) - DateTime(2000,1,1)) / i)
end

fails at i==7! ... who'd have guessed that (and would have tested for it?)??

@peteristhegreat
Copy link

peteristhegreat commented Apr 29, 2021

The most straightforward solution I found for this is to do a rounding based on the smallest increment the Dates library can handle and fits my use case. Second was small enough for me.

using Dates

days = 3.14159
period = Day(1)
if false
    period = Day(days)  # crashes with Inexact error
end

period = Second(round(Int64, days*3600*24))

# Print days ago string
date_finished_after_str = Dates.format(now() - period, Dates.ISODateTimeFormat)
println("$days ago is: $date_finished_after_str)

This explicit conversion should be allowed as a flag somewhere, maybe with some documentation about it. Or even as another version of the functions that throw Inexact.

days = 3.14159

# NOTE, these functions don't exist yet
Day(days; round_to_nearest=Millisecond)
Day(days; round_to_nearest=Second)
Day(days; round_to_nearest=Hour)
Day(days; allow_inexact=true)

@George9000
Copy link
Contributor

@peteristhegreat See this issue Add rounding support for Periods

round(x, Day) or any other period works well.

@dehann
Copy link
Contributor

dehann commented May 12, 2022

Oh, might be worth linking Discourse comment here too:

@RossBoylan
Copy link

Because of the current behavior, it is difficult to use Dates to get statistics like the average time a task takes. The seemingly natural way to do this does not work reliably:

using Dates
using Statistics
t1 = now()  #t1 is start time
sleep(2)
t2 = now()  #t2 is end of first task and start of 2nd
sleep(1)
t3 = now()  # t3 is end of 2nd task
d1 = t2-t1  # how long first task took
d2 = t3-t2 # how long 2nd task took
durations = [d1, d2]
print(durations)
print(mean(durations))  # attempt to get average time.  Fails erratically.

This fails "randomly"; here are 2 consecutive runs. The first succeeds and the second fails:

# first try
[Millisecond(2009), Millisecond(1003)]1506 milliseconds

# 2nd try
[Millisecond(2021), Millisecond(1002)]ERROR: LoadError: InexactError: Int64(1511.5)

I guess as the number of entries in durations increases so does the probability of failure.

It's unclear if there is any documented way get the average. I always get Millisecond for my now(), but the documentation does not guarantee that, much less the specific return type of subtracting 2 such DateTimes. So this code would probably work:

function meanDuration(ds)
    Millisecond(round(mean([d.value for d in ds])))
end

but it assumes the inputs are all Millisecond. Durations include CompoundPeriod which doesn't even respond to value.
One could restrict the input argument to Vector{Millisecond} for safety, but that still leaves one out of luck
on systems that don't return that (e.g., different clock precision), and for things that actually would be OK like Tuple of Milliseconds or loosely typed Vectors of Milliseconds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dates Dates, times, and the Dates stdlib module docs This change adds or pertains to documentation
Projects
None yet
Development

No branches or pull requests