Why do time quantities have to be integers?

I suspect this has been asked before, I apologize if that is the case.

Why did we decide to not allow for Dates.Second(1.5)?

1.5Unitful.s works fine for instance…

See: https://github.com/JuliaLang/julia/issues/9393

I would have preferred floats myself, but no one was willing to do the work.

4 Likes

Maybe it would be better to reopen that issue and mark it ‘up for grabs’.

1 Like

By all means, but from reading that issue, it sounds like it’s not clear if it would be such a welcomed change. I could live with the option of doing something like Second(1.5) → Millisecond(1500)… Or something similar…

Exact time algorithms require integer valued units of time.

Imagine adding convert methods that accept a Float and find the most accurate representation of that float via a Dates.CompoundPeriod, where the smallest temporal unit, Nanosecond, would need to be rounded off (and thus some accuracy would be lost)? Wouldn’t something like that keep the accuracy and allow for things like Second(1.5)?

Yes but summing many of those makes the roundoff error accumulate and that ends up with equal time intervals becoming unequal – then transactional contracts can get it wrong. For example, to buy something usually requires that the money is available before the purchase occurs – whenever words like “before” appear, the need for exact time algorithms is important. The potential for mismatch increases with signed timespans.

Yea, that makes sense. But this level of accuracy and the lack of it is well known. Whenever we use floats we know that the accuracy is limited to “single” or “double” etc… If a specific procedure requires the absolute accuracy you’re referring to then the user of that procedure (function, module, package, whatever) should be warned somehow (in the docs?). But I don’t see the harm in including the option of converting from a float to a time period.

So just curious: in the applications you’re thinking of, is temporal quantities defined only in discrete units? If so, what is the shortest quantity? Is it really nanoseconds? If not, how would these applications deal with times that are shorter than that?

(with the understanding that I do not recommend this, you can experiment)

using Base.Dates

function Base.:(*)(x::AbstractFloat, p::Period)
    n = Nanosecond(p)
    y = x * n.value
    nanos = floor(Int64, y)
    micros, nanos = fldmod(nanos, 1000)
    millis, micros = fldmod(micros, 1000)
    secs, millis = fldmod(millis, 1000)
    mins, secs = fldmod(secs, 60)
    hrs, mins = fldmod(mins, 60)
    dys, hrs = fldmod(hrs, 24)
    result = Nanosecond(nanos) + Microsecond(micros) +
                 Millisecond(millis) + Second(secs) +
                 Minute(mins) + Hour(hrs) + Day(dys)
    return result
end
julia> 1.5 * Second(1)
1 second, 500 milliseconds

julia> sqrt(2) * Day(5)
7 days, 1 hour, 42 minutes, 20 seconds, 258 milliseconds, 945 microseconds, 177 nanoseconds
2 Likes

Actually, the temporal quantities in software are modelled either using discrete points at some predetermined resolution or as half-open intervals (while you may see the opposite convention used, it really is more sound to set the open half to be earlier part and close the later part).

There are new financial regulations coming into force in 2018. For high-frequency trading, they specify timestamps resolve to 100 nanosecond increments. In order to do that properly, the time-math needs to be somewhat more resolved. 1 nanosecond time-math is handy.

To resolve timespans finer than the smallest period supported by an encoding is difficult when not impossible. It is far easier to extend the refinement of the underlying struct.

Cool. So would you give your blessings to something like this?

  • algorithms that need absolute accuracy should only accept actual discrete time periods, in their method signature. So they can’t accept floats or some such.
  • algorithms that don’t rely on such high levels of accuracy can accept:
    1. a Number where their docs say it should be seconds/minutes/etc…
    2. a Unitful.jl time unit
    3. DatesTime/Time instance

I think #2 is best in the case where accuracy is not an issue. This way whoever needs the accuracy can enforce it, and whoever wants some flexibility can have it.

Give me two examples: an algorithm and a task where you want the less careful approach? What if the less careful approach takes just as long to compute as the careful approach?

1 Like

I’m not sure what you’re getting at, do you want to prove that any task or algorithm I could come up with is solvable without the option of converting a float to a discrete time period? That’s not very convincing. We can agree that the universe of all the algorithms and tasks that ever were or will be, may include things that neither of us thought of, yet.

My main concern with this post is finding out if there is a reason not to include the option of converting a float to a time period. Is it horrible? Will no one use it? Is it counter productive? Etc…

Your task actually helped me realize something: the float I want to convert comes from somewhere. We almost always know what this float is supposed to count (seconds, minutes, etc). We can therefore stick that float inside a Unitful.Quantity with the correct unit. So I think that this functionality of converting a float to a Dates.TimePeriod should reside in Unitful.jl:

source spits out hours as floats -> Float64(1.2) -> `Unitful.hr` -> `Dates.CompoundPeriod`

Times and dates a mess, and different applications have different requirements. Instead of arguing with people about their requirements, why don’t you just implement yours in a package? This does not need to be in Base.

(arguably, even Base.Dates could be a package, but I did not find a related “move * to a package” issue)

That’s what I concluded with in my last post here. But just to be clear: I don’t think I’m arguing with @JeffreySarnoff about @JeffreySarnoff’s requirements, I’m trying to figure out if there is a true reason to why there is no conversion from floats to times. Now that I’ve cleared that off I can implement it. I.e. copy paste @JeffreySarnoff’s code from above and adding it to Unitful.jl

Float64 values represent integers up to 2^53 exactly. The idea that integer operations incur roundoff errors in floating-point arithmetic is a myth.

Of course, integers over 2^53 may be rounded, but with Int64 you will get even more catastrophic overflow effects for integers over 2^63. What time calculations require exact integers over 2^53 but are okay with disasters over 2^63?

4 Likes
1 Like

Sorry, then I misunderstood. Asking for his “blessings” looked like you wanted his approval for something.

I did. He is of the opinion (if I got him right) that such functionality would be some how bad. I wanted to know how bad. But now I know, it’s probably not that bad.
I’m always afraid that I missed some important reason to do/not do something new. That’s why I ask first here.

Float64 values represent integers up to 2^53 exactly. The idea that integer operations incur roundoff errors in floating-point arithmetic is a myth.

We are talking about two different things.

My preference comes from a desire to make the unexpected unlikely. Once one allows floating-point operands, it is difficult to assure that their use will be limited to floating-point conversions of integer values.