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.
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.
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
Related
Running this code (as a built executable, not with the debugger):
package main
import (
"fmt"
"time"
)
func main() {
startTime := time.Now()
for i := 0; i < 1000; i++ {
time.Sleep(1 * time.Millisecond)
}
fmt.Printf("%d\n", time.Since(startTime).Milliseconds())
}
I get the output:
15467
This seems like a major overhead for calling the time.Sleep() function; it's essentially taking 15ms per loop iteration, even though it's only sleeping for 1ms in each loop. This suggests that there's a 14ms overhead for running an iteration of the loop and initiating a sleep.
If we adjust the sleep duration:
package main
import (
"fmt"
"time"
)
func main() {
startTime := time.Now()
for i := 0; i < 1000; i++ {
time.Sleep(10 * time.Millisecond)
}
fmt.Printf("%d\n", time.Since(startTime).Milliseconds())
}
I get the output:
15611
This is essentially the same duration, even though it should be sleeping for 10x as long. This kills the idea that there's a 14ms overhead for the loop iteration and initiating the sleep, because if that were the case, it would be (14+10)*1000 = 24000ms total, which it is not.
What am I missing? Why would this code take the same duration to execute, whether the sleep duration is 1ms or 10ms?
Note that I've tried running this in the Go playground but get different results; I think it handles sleeping differently. These results are consistent on my laptop, which is running an i7-10510.
It is probably related to the frequency of the system's timer. For example, on Windows the clock ticks every 15 milliseconds (source):
For example, for Windows running on an x86 processor, the default interval between system clock ticks is typically about 15 milliseconds, and the minimum interval between system clock ticks is about 1 millisecond. Thus, the expiration time of a default-resolution timer (which ExAllocateTimer creates if the EX_TIMER_HIGH_RESOLUTION flag is not set) can be controlled only to within about 15 milliseconds, but the expiration time of a high-resolution timer can be controlled to within a millisecond.
If you need a higher precision timer you probably need to find a way to use High-Resolution Timers.
More information can be found in the threads below:
https://github.com/golang/go/issues/44343
https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/
https://github.com/golang/go/issues/44343
I have two function calls that I would like to separate by at least a nanosecond. But I want the delay to be as small as possible.
The code below shows an empty for loop is much more efficient at this than using time.Sleep(time.Nanosecond)
Is there an even more efficient way to guarantee at least one nanosecond has elapsed?
func TimeWaster () {
start := uint64(time.Now().UnixNano())
stop := uint64(time.Now().UnixNano())
fmt.Println(time.Duration(stop-start))//0s
//no nanoseconds pass
start = uint64(time.Now().UnixNano())
time.Sleep(time.Nanosecond)
stop = uint64(time.Now().UnixNano())
fmt.Println(time.Duration(stop-start))//6.9482ms
//much *much* more than one nanosecond passes
start = uint64(time.Now().UnixNano())
for uint64(time.Now().UnixNano()) == start {
//intentionally empty loop
}
stop = uint64(time.Now().UnixNano())
fmt.Println(time.Duration(stop-start))//59.3µs
//much quicker than time.Sleep(time.Nanosecond), but still much slower than 1 nanosecond
}
The package you're using strangely enforces uniqueness of values by time, so all you need to do is loop until the time package is no longer reporting the same value for the current nanosecond. This doesn't happen after 1 nanosecond, in fact the resolution of the UnixNano is about 100 nanoseconds on my machine and only updates about every 0.5 milliseconds.
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println(time.Now().UnixNano())
smallWait()
fmt.Println(time.Now().UnixNano())
}
func smallWait() {
for start := time.Now().UnixNano(); time.Now().UnixNano() == start; {}
}
The loop is pretty self-explanatory, just repeat until the UnixNano() is different
Go has methods to extract almost every component of a timestamp, eg time.Second(), time.Nano(), but none to extract the millisecond portion of a timestamp.
How does one extract the millisecond value of a timestamp.
eg, in the case of a timestamp like:
2021-01-07 10:33:06.511
i want to extract 511
To access the fraction seconds, you may use time.Nanosecond(). And if we convert it to time.Duration (time.Duration is exactly the nanoseconds count), we can take advantage of its Duration.Milliseconds() method (which of course does no magic but code will be clearer and easier to read):
func extractMs(t time.Time) int64 {
return time.Duration(t.Nanosecond()).Milliseconds()
}
Try it on the Go Playground.
there is an answer in the comments, but i want to post here to be cannonical:
func extractMillisecond(t time.Time) int {
ms := time.Duration(t.Nanosecond()) / time.Millisecond
return int(ms)
}
In my RESTFUL web service which is an online game, I'm storing starting time of every question in an global variable like this: var MyTime time.Time which I should update it after every level of the game. My application is distributed, so I want to make sure all of my apps are not updating it at the same time. That's why I've decided to make it atomic.
Actually I'm familiar with Golang sync/atomic package.
I tried to use atomic.LoadPointer() method but it needs specific argument type which isn't safe. Do you any other way for this?
Update:
Okay I solved my problem like this.
I defined time variable as atomic.Value and used atomic Load and Store methods. This is the code:
var myTime atomic.Value
myTime.Store(newTime) and load myTime.Load().(time.Time).
Consider that Load() method returns interface, so you should write (time.Time) at the end in order to convert it to time.Time type.
This can't be done, as such, because time.Time is a compound type:
type Time struct {
// wall and ext encode the wall time seconds, wall time nanoseconds,
// and optional monotonic clock reading in nanoseconds.
//
// From high to low bit position, wall encodes a 1-bit flag (hasMonotonic),
// a 33-bit seconds field, and a 30-bit wall time nanoseconds field.
// The nanoseconds field is in the range [0, 999999999].
// If the hasMonotonic bit is 0, then the 33-bit field must be zero
// and the full signed 64-bit wall seconds since Jan 1 year 1 is stored in ext.
// If the hasMonotonic bit is 1, then the 33-bit field holds a 33-bit
// unsigned wall seconds since Jan 1 year 1885, and ext holds a
// signed 64-bit monotonic clock reading, nanoseconds since process start.
wall uint64
ext int64
// loc specifies the Location that should be used to
// determine the minute, hour, month, day, and year
// that correspond to this Time.
// The nil location means UTC.
// All UTC times are represented with loc==nil, never loc==&utcLoc.
loc *Location
}
However, you can do this with pointers, so *time.Time would be possible, if this suits your needs. But of course, this is discouraged, by virtue of the fact that atomic.LoadPointer and atomic.StorePointer use the unsafe package to accomplish their magic.
A much better approach, if it will work for you, is just to use a mutex to protect your value. There are many ways to do this, but one minimal example:
type MyTime struct {
t time.Time
mu sync.RWMutex
}
func (t *MyTime) Time() time.Time {
t.mu.RLock()
defer t.mu.RUnlock()
return t.t
}
func (t *MyTime) SetTime(tm time.Time) {
t.mu.Lock()
defer t.mu.Unlock()
t.t = tm
}
You can keep unix time https://golang.org/pkg/time/#example_Time_Unix as atomic which is int64. Then convert to go time after you've read atomic value.
If you are only interested in the timestamp, you could simply keep a reference to the unix time, which is int64 and atomically update that.
var ts int64
func main() {
atomic.StoreInt64(&ts, time.Now().Unix())
t := time.Unix(atomic.LoadInt64(&ts), 0)
fmt.Println(t)
}
Instead, if you need the entire Time struct, read on.
Go 1.19 (still in beta)
If you are okay with storing a pointer to a time.Time object, you can use atomic.Pointer, which is a generic struct that abstracts atomic.LoadPointer and atomic.StorePointer. As a simple example:
// instantiate generic struct with time.Time
var at = atomic.Pointer[time.Time]{}
func main() {
t := time.Now()
at.Store(&t)
t = *at.Load()
fmt.Println(t)
}
Playground: https://go.dev/play/p/KwTMgvJIenx?v=gotip
Note that in Go 1.19 you can also use atomic.Int64. The advantage of these atomic types instead of top-level functions is that it's more fool-proof; it's impossible to access the value non-atomically as it's hidden behind the struct.
I've written a code snipped that creates a timer with a 0 length time, and it does not immediately expire (which is what I expected). A very short sleep call does make it expire, but I'm confused as to why.
The reason I care is that the code using this idea has a snippet that returns 0 on a low probability error, with the idea that the timer should be set to immediately expire, and retry a function. I do not believe that the nanosecond sleep needed here will affect my implementation, but it bothers me.
Did I make a mistake, is this expected behaviour?
Thanks!
package main
import (
"fmt"
"time"
)
func main() {
testTimer := time.NewTimer(time.Duration(0) * time.Millisecond)
fmt.Println(Expired(testTimer))
time.Sleep(time.Nanosecond)
fmt.Println(Expired(testTimer))
}
func Expired(T *time.Timer) bool {
select {
case <-T.C:
return true
default:
return false
}
}
Playground link: https://play.golang.org/p/xLLHoR8aKq
Prints
false
true
time.NewTimer() does not guarantee maximum wait time. It only guarantees a minimum wait time. Quoting from its doc:
NewTimer creates a new Timer that will send the current time on its channel after at least duration d.
So passing a zero duration to time.NewTimer(), it's not a surprise the returned time.Timer is not "expired" immediately.
The returned timer could be "expired" immediately if the implementation would check if the passed duration is zero, and would send a value on the timer's channel before returning it, but it does not. Instead it starts an internal timer normally as it does for any given duration, which will take care of sending a value on its channel, but only some time in the future.
Note that with multiple CPU cores and with runtime.GOMAXPROCS() being greater than 1 there is a slight chance that another goroutine (internal to the time package) sends a value on the timer's channel before NewTimer() returns, but this is a very small chance... Also since this is implementation detail, a future version might add this "optimization" to check for 0 passed duration, and act as you expected it, but as with all implementation details, don't count on it. Count on what's documented, and expect no more.
Go's timer functions guarantee to sleep at least the specified time. See the docs for Sleep and NewTimer respectively:
Sleep pauses the current goroutine for at least the duration d. A negative or zero duration causes Sleep to return immediately.
NewTimer creates a new Timer that will send the current time on its channel after at least duration d.
(emphasis added)
In your situation, you should probably just not use a timer in the situation that you don't want to sleep at all.
This is due to the internal time it takes to set up the timer object. If you'll note in the playground link below the timer does expire at the proper time, but the internal go routine that sets it up and starts it takes longer than your Expire function does to check it.
When the Timer expires, the current time will be sent on C (the channel)
So you'll notice that after it expires, it still sends the original time, because it has expired even before the nanosecond Sleep finished.
https://play.golang.org/p/Ghwq9kJq3J
package main
import (
"fmt"
"time"
)
func main() {
testTimer := time.NewTimer(0 * time.Millisecond)
Expired(testTimer)
time.Sleep(time.Nanosecond)
Expired(testTimer)
n := time.Now()
fmt.Printf("after waiting: %d\n", n.UnixNano())
}
func Expired(T *time.Timer) bool {
select {
case t:= <-T.C:
fmt.Printf("expired %d\n", t.UnixNano())
return true
default:
n := time.Now()
fmt.Printf("not expired: %d\n", n.UnixNano())
return false
}
}