Creating a time.Duration from float64 seconds
Asked Answered
M

3

19

I have a float64 containing a duration in seconds. I'm looking for a way to convert this value to a time.Duration. I'm able to perform this conversion, but I'm wondering if there is not a more elegant way.

The approach I have is this:

    var timeout float64 // input value of float type

    var res time.Duration // result value of time.Duration type

    res += time.Duration(math.Round(timeout)) * time.Second
    timeout -= math.Round(timeout)
    timeout *= 1000
    res += time.Duration(math.Round(timeout)) * time.Millisecond
    timeout -= math.Round(timeout)
    timeout *= 1000
    res += time.Duration(math.Round(timeout)) * time.Microsecond
    timeout -= math.Round(timeout)
    timeout *= 1000
    res += time.Duration(math.Round(timeout)) * time.Nanosecond

    return res

What I dislike about this is that it is cumbersome and not reliable. I'd expect Go to supply something like this out of the box and to perform these conversions in a way that detects overflows and similar range violations. It seems that Go is not there yet, but maybe I missed something obvious, hence my question.

Notes:

  • This question doesn't address my needs, because it is rather related to the opposite way of conversion. That conversion is actually pretty painless, which makes the it even more surprising that the one I need isn't there.
  • Why don't I use milliseconds instead? Simple reason: Consistency, KISS principle, principle of least surprise. The SI unit for time is the second. Everything else is only derived from this, so I use this as a default.
  • Nitpick concerning the previous statement: Go itself says "There is no definition for units of Day or larger to avoid confusion across daylight savings time zone transitions.". They missed the point, because they still have minutes and hours, even though there are minutes with 58..61 seconds. Not a big deal, just mentioning it for completeness.
Massive answered 27/5, 2021 at 16:23 Comment(4)
What's wrong with return time.Duration(timeout * float64(time.Second))?Allen
Nothing in particular, apart from the fact that it also doesn't check for range violations. At one point I had time.Duration(time.Duration(event.Timeout) * time.Second), which worked but triggered me to investigate better alternatives.Massive
time.Duration is a simple integer type, so any validation is just intrinsic to the numeric values in the language specification. This usually doesn't arise because input generally needs validation somewhere to begin with, leaving it up to the programmer to decide what is valid --e.g. a program may not expect negative durations at all which are perfectly valid, or may not expect durations of hundreds of years expressed in seconds.Allen
Don't get me wrong, I'd accept your comment as answer. In addition, it seems there is no better way out of the box in Go 1, which concludes my search.Massive
S
34

As JimB's comment shows, multiply the number of seconds by the number of duration units per second. Durations are measured in nanoseconds, but your code does not need to know that detail.

return time.Duration(timeout * float64(time.Second))

Convert to floating point for the multiplication and convert to duration to get the result.

Snorter answered 27/5, 2021 at 16:23 Comment(0)
S
0

I'm not sure what the issue is here. Your request is very simple to implement:

package main

import (
   "fmt"
   "time"
)

func duration(f float64) time.Duration {
   return time.Duration(f * 1e9)
}

func main() {
   t := duration(9)
   fmt.Println(t) // 9s
}

It's literally one line of code, what is not elegant about that? The only way it could be more elegant, is if time.Duration was float64 natively. And that just doesn't make sense, as Go doesn't track anything smaller than a nanosecond.

https://golang.org/pkg/time#Duration

Sadler answered 27/5, 2021 at 20:54 Comment(2)
You'll hate me for it, but it's not that trivial either: time.Duration(f) truncates the floating-point value to an integer, so duration(1.5) will give you one second.Massive
Yep, now it works. The only downside I perceive is that 1e9 seems to me like an implementation detail. The solution by @Allen using float64(time.Second) achieves the same but without relying on the nanosecond resolution.Massive
M
-1
time.Duration(timeout) * time.Second

per the docs at https://pkg.go.dev/time#Second

Manes answered 17/7, 2023 at 18:29 Comment(1)
Nope, you didn't understand the issue: Since timeout can be 0.1 and time.Duration is an alias to int64, this will perform a truncating conversion to an integral type and discard the digits right of the radix separator. In short, the result is zero, not 100ms or 0.1s as it should.Massive

© 2022 - 2024 — McMap. All rights reserved.