483

To test concurrent goroutines, I added a line to a function to make it take a random time to return (up to one second)

time.Sleep(rand.Int31n(1000) * time.Millisecond)

However when I compiled, I got this error

.\crawler.go:49: invalid operation: rand.Int31n(1000) * time.Millisecond (mismatched types int32 and time.Duration)

Any ideas? How can I multiply a duration?

0xC0DED00D
  • 19,522
  • 20
  • 117
  • 184
Colonel Panic
  • 132,665
  • 89
  • 401
  • 465

9 Answers9

724

int32 and time.Duration are different types. You need to convert the int32 to a time.Duration:

time.Sleep(time.Duration(rand.Int31n(1000)) * time.Millisecond)
Błażej Michalik
  • 4,474
  • 40
  • 55
mna
  • 22,989
  • 6
  • 46
  • 49
  • 6
    Thanks that worked. I also learnt about seeding the random number generator `rand.Seed(time.Now().Unix())` – Colonel Panic Jul 10 '13 at 14:51
  • 114
    just for my knowledge, how does the following work then? `time.Sleep(time.Second * 2)` – Ishan Khare Aug 27 '15 at 13:25
  • 131
    It works because constants have an adaptive type, based on how they are used. See this blog post by Rob Pike that explains it in detail: http://blog.golang.org/constants – mna Aug 28 '15 at 14:12
  • 56
    This is one of those weird things in go due to its simplistic type system (lack of operator overloading in this case) - you have to cast the multiplication to `Duration` * `Duration` = `Duration`, instead of the original one which actually makes more sense: `Duration` * `int` = `Duration`. – Timmmm Jan 05 '16 at 14:49
  • 4
    @Timmmm This is not a matter of "simplistic type system", it's a question of "implicit numeric conversion". Read more in the [FAQ: Why does Go not provide implicit numeric conversions?](https://golang.org/doc/faq#conversions) – icza Jul 21 '16 at 12:11
  • 37
    Well either operator overloading or implicit numeric conversion would let this work. I think they were right to leave implicit numeric conversion out. After looking back on this that `int64(...) * Duration` makes much more sense than casting to `Duration`, which is just a basic violation of how units should work. Sadly that doesn't work. You really have to do `Duration * Duration` which is horrible. – Timmmm Jul 21 '16 at 12:22
  • 53
    This is horrible. You are never advised to multiply a `time_t` by `time_t` in C even if you can. This is not only a simplistic type system, it's confusing and horrible API design. – lilydjwg Jan 23 '18 at 15:24
  • 15
    Yeah this is flat out ill. Duration times duration means what? – Sentinel Dec 03 '18 at 17:44
  • 23
    It's also very confusing that `1 * time.Second` works whereas `secs * time.Second` (where `secs` is an integer) fails. I'm trying to teach Go to my kids, but it's just too inconsistent and counter-intuitive in too many ways. – Fixee Dec 24 '18 at 03:35
  • @ColonelPanic For better randomization, use UnixNano() instead: rand.Seed(time.Now().UnixNano()). Also, for others, keep in mind you don't need to randomize it each time we generate a number, you can safely call it once in the main function and we are done. – Melardev Jul 16 '20 at 11:18
  • See also https://golang.org/issue/20757 (“proposal: time: make Duration safer to use”) and the common errors enumerated there. When you're dealing with integers, you may need to watch out for forgotten scaling, accidental double-scaling, and unexpected overflow. – bcmills Jul 12 '21 at 14:59
  • "̶R̶e̶m̶e̶m̶b̶e̶r̶,̶ ̶t̶h̶e̶r̶e̶ ̶i̶s̶ ̶n̶o̶ ̶m̶a̶g̶i̶c̶ ̶i̶n̶ ̶G̶o̶.̶"̶ – Brendano257 Apr 27 '22 at 17:28
84

You have to cast it to a correct format Playground.

yourTime := rand.Int31n(1000)
time.Sleep(time.Duration(yourTime) * time.Millisecond)

If you will check documentation for sleep, you see that it requires func Sleep(d Duration) duration as a parameter. Your rand.Int31n returns int32.

The line from the example works (time.Sleep(100 * time.Millisecond)) because the compiler is smart enough to understand that here your constant 100 means a duration. But if you pass a variable, you should cast it.

Salvador Dali
  • 214,103
  • 147
  • 703
  • 753
  • 9
    That's nice, but why is it possible to multiply a `Duration` by a raw integer (not in a variable), like: `time.Sleep(100 * time.Millisecond)` AFAIK, 100 is not a `Duration` – carnicer Jul 05 '20 at 12:01
  • @carnicer Actually, 100 is, like all "raw integers", an `untyped int`, which can be used as any integer-based type (including `Duration` and all the primitive integer types, among others) and will be implicitly converted when used. This only works for constants. The idea is that when you type out a constant, it's unnecessary to have you define the exact type since it's clear at compile time from the context. If you multiply a constant with a `uint16`, you clearly mean the constant to be a `uint16`, likewise when you multiply it with a `Duration`. – scenia Dec 19 '22 at 19:24
26

In Go, you can multiply variables of same type, so you need to have both parts of the expression the same type.

The simplest thing you can do is casting an integer to duration before multiplying, but that would violate unit semantics. What would be multiplication of duration by duration in term of units?

I'd rather convert time.Millisecond to an int64, and then multiply it by the number of milliseconds, then cast to time.Duration:

time.Duration(int64(time.Millisecond) * int64(rand.Int31n(1000)))

This way any part of the expression can be said to have a meaningful value according to its type. int64(time.Millisecond) part is just a dimensionless value - the number of smallest units of time in the original value.

If walk a slightly simpler path:

time.Duration(rand.Int31n(1000)) * time.Millisecond

The left part of multiplication is nonsense - a value of type "time.Duration", holding something irrelevant to its type:

numberOfMilliseconds := 100
// just can't come up with a name for following:
someLHS := time.Duration(numberOfMilliseconds)
fmt.Println(someLHS)
fmt.Println(someLHS*time.Millisecond)

And it's not just semantics, there is actual functionality associated with types. This code prints:

100ns
100ms

Interestingly, the code sample here uses the simplest code, with the same misleading semantics of Duration conversion: https://golang.org/pkg/time/#Duration

seconds := 10

fmt.Print(time.Duration(seconds)*time.Second) // prints 10s

Community
  • 1
  • 1
George Polevoy
  • 7,450
  • 3
  • 36
  • 61
14

It's nice that Go has a Duration type -- having explicitly defined units can prevent real-world problems.

And because of Go's strict type rules, you can't multiply a Duration by an integer -- you must use a cast in order to multiply common types.

/*
MultiplyDuration Hide semantically invalid duration math behind a function
*/
func MultiplyDuration(factor int64, d time.Duration) time.Duration {
    return time.Duration(factor) * d        // method 1 -- multiply in 'Duration'
 // return time.Duration(factor * int64(d)) // method 2 -- multiply in 'int64'
}

The official documentation demonstrates using method #1:

To convert an integer number of units to a Duration, multiply:

seconds := 10
fmt.Print(time.Duration(seconds)*time.Second) // prints 10s

But, of course, multiplying a duration by a duration should not produce a duration -- that's nonsensical on the face of it. Case in point, 5 milliseconds times 5 milliseconds produces 6h56m40s. Attempting to square 5 seconds results in an overflow (and won't even compile if done with constants).

By the way, the int64 representation of Duration in nanoseconds "limits the largest representable duration to approximately 290 years", and this indicates that Duration, like int64, is treated as a signed value: (1<<(64-1))/(1e9*60*60*24*365.25) ~= 292, and that's exactly how it is implemented:

// A Duration represents the elapsed time between two instants
// as an int64 nanosecond count. The representation limits the
// largest representable duration to approximately 290 years.
type Duration int64

So, because we know that the underlying representation of Duration is an int64, performing the cast between int64 and Duration is a sensible NO-OP -- required only to satisfy language rules about mixing types, and it has no effect on the subsequent multiplication operation.

If you don't like the the casting for reasons of purity, bury it in a function call as I have shown above.

Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
10

Confused by some of the comments & discussions on multiplying Duration with Duration, I played around a bit with the units and functions and got this:

time.Second =  1s
time.Minute =  1m0s

time.Duration(1) = 1ns
time.Duration(1) * time.Millisecond =  1ms
time.Duration(1) * time.Second =  1s
time.Duration(1) * time.Minute =  1m0s
Catsy
  • 1,160
  • 1
  • 9
  • 12
2

Just multiply it like this:

time.Sleep(1000 * time.Millisecond)

You don't need to convert it.


Reasoning:

1000 is an untyped literal constant with a default type of integer, it has an ideal type.

1000 * time.Millisecond can be used directly because, Go converts the untyped constants to numeric types automatically. Here, it converts 1000 to time.Duration automatically, because it's an alias to Int64.

Millisecond defined like this:

type Duration int64

const (
    Nanosecond  Duration = 1
    Microsecond          = 1000 * Nanosecond
    Millisecond          = 1000 * Microsecond
    Second               = 1000 * Millisecond
    Minute               = 60 * Second
    Hour                 = 60 * Minute
)

Millisecond has time.Duration type, but, underlying, it's an int64 which is assignable and can be used by a numeric untyped integer.


I wrote about these details here in this post.

Go Default Types Cheatsheet

Inanc Gumus
  • 25,195
  • 9
  • 85
  • 101
0

My turn:

https://play.golang.org/p/RifHKsX7Puh

package main

import (
    "fmt"
    "time"
)

func main() {
    var n int = 77
    v := time.Duration( 1.15 * float64(n) ) * time.Second

    fmt.Printf("%v %T", v, v)
}

It helps to remember the simple fact, that underlyingly the time.Duration is a mere int64, which holds nanoseconds value.

This way, conversion to/from time.Duration becomes a formality. Just remember:

  • int64
  • always nanosecs
Dharman
  • 30,962
  • 25
  • 85
  • 135
latitov
  • 432
  • 3
  • 8
0

You can use time.ParseDuration.

ms := rand.Int31n(1000)
duration, err := time.ParseDuration(fmt.Sprintf(
    "%vms",
    ms,
))
Amaimersion
  • 787
  • 15
  • 28
-4

For multiplication of variable to time.Second using following code

    oneHr:=3600
    addOneHrDuration :=time.Duration(oneHr)
    addOneHrCurrTime := time.Now().Add(addOneHrDuration*time.Second)
  • This not a good way of using Go's `time.Duration` variables. You name your variable `addOneHrDuration` of time `time.Duration` but then proceed to set it to 3600 ns not to one hour. A `time.Duration` happens to have base units of nanoseconds. To actually get a one hour duration you could do something like: `const oneHourDuration = 60 * time.Hour` (or `3600 * time.Second` or `time.Hour`). – Dave C Aug 09 '19 at 11:11