Question

How to Multiply time.Duration by an Integer in Go

gotime

Question

I am testing concurrent goroutines in Go and want a function to pause for a random amount of time, up to one second. I added this line:

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

But the code does not compile, and I get this error:

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

Why does this happen, and what is the correct way to multiply a duration by an integer in Go?

Short Answer

By the end of this page, you will understand what time.Duration is in Go, why it cannot be multiplied directly by values of a different numeric type, and how to convert integers safely so they work with functions like time.Sleep. You will also see practical examples using random delays, learn common mistakes, and understand how this pattern appears in real Go code.

Concept

time.Duration in Go is a named type that represents a length of time as a number of nanoseconds.

For example:

var d time.Duration = time.Second

Even though time.Duration is based on an integer type internally, Go treats it as its own type. Go is strict about type safety, so values of different types usually cannot be mixed in arithmetic unless you convert them explicitly.

That is why this fails:

rand.Int31n(1000) * time.Millisecond
  • rand.Int31n(1000) returns an int32
  • time.Millisecond is a time.Duration
  • Go does not automatically convert int32 to time.Duration

The fix is to convert the random number to time.Duration first:

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

Now both sides of the multiplication are compatible:

  • time.Duration(rand.Int31n(1000)) is a time.Duration
  • time.Millisecond is also a time.Duration

This matters in real programming because durations are used everywhere in Go:

  • timeouts
  • retries
  • polling intervals
  • backoff logic
  • rate limiting
  • benchmarking and simulation

Understanding how Go handles typed values helps you avoid compile errors and write clearer code.

Mental Model

Think of time.Duration as a measuring unit like centimeters.

If someone gives you the number 5, that is just a plain number. But if you want to say 5 centimeters, you must attach the unit.

In Go:

  • rand.Int31n(1000) is just a number of type int32
  • time.Millisecond is a time unit

Go does not guess that your plain number should become a duration. You must say it explicitly:

time.Duration(rand.Int31n(1000))

Now Go understands: “This number represents a duration count,” so multiplying by time.Millisecond makes sense.

So the mental model is:

  • plain integers are just counts
  • time.Duration is a count with time meaning
  • convert the count before combining it with time units
Take Quiz

Syntax and Examples

The basic pattern is:

time.Duration(number) * time.Unit

Correct syntax

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

This means:

  • generate a random number from 0 to 999
  • convert it to time.Duration
  • multiply it by time.Millisecond
  • sleep for that amount of time

Example: fixed duration multiplication

package main

import (
	"fmt"
	"time"
)

func main() {
	wait := time.Duration(3) * time.Second
	fmt.Println(wait)
}

Output:

3s

Example: random delay

package main

import (
	"math/rand"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano())

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

Example: using rand.Intn instead

A common alternative is to use rand.Intn, which returns int instead of int32:

delay := time.Duration(rand.Intn(1000)) * time.Millisecond

This still requires conversion to time.Duration, but many Go programs prefer Intn when int32 is not specifically needed.

Step by Step Execution

Consider this code:

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano())

	n := rand.Int31n(5)
	delay := time.Duration(n) * time.Millisecond

	fmt.Println("random number:", n)
	fmt.Println("delay:", delay)

	time.Sleep(delay)
	fmt.Println("done")
}

Step by step:

  1. rand.Seed(time.Now().UnixNano())

    • Initializes the random generator with a changing seed.
    • Without this, the program may produce the same sequence each run.
  2. n := rand.Int31n(5)

    • Picks a random int32 from 0 to 4.
    • Example value: 3
  3. delay := time.Duration(n) * time.Millisecond

    • Converts 3 from int32 to time.Duration
    • Multiplies it by time.Millisecond
    • Result: 3ms
  4. fmt.Println("random number:", n)

    • Prints the original random number
  5. fmt.Println("delay:", delay)

    • Prints the duration, such as 3ms
  6. time.Sleep(delay)

    • Pauses the program for that duration
  7. fmt.Println("done")

    • Runs after the sleep finishes

The key step is the conversion:

time.Duration(n)

Without that conversion, the multiplication fails at compile time.

Real World Use Cases

Working with multiplied durations is common in Go applications.

Random delays for testing

You used this pattern for goroutine testing. That is a valid use case when you want to simulate:

  • network latency
  • slow database queries
  • non-deterministic task completion

Example:

time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)

Retry with backoff

Programs often wait longer after each failed attempt:

wait := time.Duration(attempt) * time.Second

Polling loops

An app may check a resource every few seconds:

interval := time.Duration(10) * time.Second

Rate limiting

A worker may pause between operations:

time.Sleep(200 * time.Millisecond)

Timeout configuration

Sometimes you compute durations from numeric config values:

secondsFromConfig := 15
%timeout := time.Duration(secondsFromConfig) * time.Second

This pattern is especially useful when values come from:

  • config files
  • environment variables
  • command-line flags
  • user input
Take Quiz

Real Codebase Usage

In real Go codebases, developers usually compute durations in a small, readable variable first instead of putting everything directly inside time.Sleep.

Common pattern: assign first, use later

delay := time.Duration(rand.Intn(1000)) * time.Millisecond
time.Sleep(delay)

This is easier to debug and log.

Validation pattern

When durations come from config, developers often validate them:

seconds := 5
if seconds < 0 {
	seconds = 0
}
timeout := time.Duration(seconds) * time.Second

Backoff and retry logic

for attempt := 1; attempt <= 3; attempt++ {
	err := tryRequest()
	if err == nil {
		return
	}

	wait := time.Duration(attempt) * time.Second
	time.Sleep(wait)
}

Jitter to avoid synchronized retries

In distributed systems, developers add randomness so many processes do not retry at exactly the same moment:

base := 2 * time.Second
jitter := time.Duration(rand.Intn(500)) * time.Millisecond
time.Sleep(base + jitter)

Guarding against bad units

A common real-world bug is forgetting the unit multiplier. Good code makes units explicit:

// Good
pollInterval := time.Duration(cfg.PollMs) * time.Millisecond

// Risky: meaning of 500 is unclear
pollInterval := time.Duration(500)

The first version is much clearer because it documents the intended unit.

Common Mistakes

1. Mixing numeric types without conversion

Broken code:

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

Why it fails:

  • rand.Int31n(1000) is int32
  • time.Millisecond is time.Duration

Fix:

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

2. Forgetting the time unit

Broken code:

time.Sleep(time.Duration(1000))

This sleeps for 1000 nanoseconds, not 1000 milliseconds.

Fix:

time.Sleep(time.Duration(1000) * time.Millisecond)

3. Assuming Go does automatic conversion

Broken idea:

var n int = 5
var d time.Duration = n * time.Second

This does not compile because n is int, not time.Duration.

Fix:

var n int = 5
var d time.Duration = time.Duration(n) * time.Second

4. Using random delays without seeding

Code:

rand.Intn(1000)

This may repeat the same sequence across runs depending on how the random generator is used.

Typical fix:

rand.Seed(time.Now().UnixNano())

5. Writing hard-to-read one-liners

This works, but is less readable:

time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)

Often clearer:

delay := time.Duration(rand.Intn(1000)) * time.Millisecond
time.Sleep(delay)

This is easier to inspect and test.

Comparisons

ConceptWhat it isExampleNotes
intGeneral integer type5Used for counts, indexes, lengths
int3232-bit integerrand.Int31n(1000)Not interchangeable with time.Duration
time.DurationTime length in nanoseconds2 * time.SecondUsed by time.Sleep, timeouts, intervals

time.Duration vs plain integers

Plain integertime.Duration
Just a numberA number with time meaning
Cannot be passed directly to time APIs in mixed arithmeticDesigned for time calculations
May represent anythingRepresents a duration in nanoseconds

rand.Int31n vs rand.Intn

FunctionReturn typeExample use
rand.Int31n(n)int32Useful when you specifically want int32
rand.Intn(n)intCommon choice for general use

For duration calculations, both still need conversion:

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

Direct duration literal vs computed duration

StyleExampleBest for
Direct500 * time.MillisecondFixed delays
Computedtime.Duration(n) * time.MillisecondDynamic values from variables or random numbers
Take Quiz

Cheat Sheet

Core rule

When combining numbers with time.Duration, convert the number first.

time.Duration(n) * time.Millisecond

Common examples

wait := time.Duration(5) * time.Second
sleep := time.Duration(rand.Intn(1000)) * time.Millisecond
timeout := time.Duration(configSeconds) * time.Second

Why your original code failed

rand.Int31n(1000) * time.Millisecond
  • left side: int32
  • right side: time.Duration
  • Go does not mix them automatically

Correct version

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

Remember the unit

time.Duration(1000)              // 1000 nanoseconds
time.Duration(1000) * time.Millisecond // 1000 milliseconds

Useful units

time.Nanosecond
time.Microsecond
time.Millisecond
time.Second
time.Minute
time.Hour

Readable pattern

delay := time.Duration(rand.Intn(1000)) * time.Millisecond
time.Sleep(delay)

Safe habit

If a value is meant to represent time, make the unit explicit.

FAQ

Why do I need to convert to time.Duration in Go?

Because Go does not automatically mix different numeric types in arithmetic. time.Duration is a distinct type.

What type is time.Millisecond?

It is a constant of type time.Duration.

Does rand.Intn() also need conversion?

Yes. Even though it returns int, you still need time.Duration(rand.Intn(...)) before multiplying by a duration unit.

What happens if I write time.Duration(1000)?

That means 1000 nanoseconds, because time.Duration stores nanoseconds unless you multiply by a unit like time.Millisecond.

Is 500 * time.Millisecond valid without conversion?

Yes. Untyped numeric constants like 500 can work directly with time.Duration constants.

Should I use rand.Int31n or rand.Intn?

Usually rand.Intn is simpler unless you specifically need an int32 result.

Can I add durations together?

Yes.

wait := 2*time.Second + 500*time.Millisecond

Can I divide durations in Go?

Yes.

half := 10 * time.Second / 2
Take Quiz

Mini Project

Description

Build a small Go program that simulates multiple worker goroutines finishing at random times. This demonstrates how to create computed delays with time.Duration and use them safely with time.Sleep.

Goal

Create a program that starts several goroutines, gives each one a random delay in milliseconds, and prints when each worker starts and finishes.

Requirements

[ "Start at least 3 goroutines.", "Each goroutine must sleep for a random duration between 0 and 999 milliseconds.", "Use time.Duration(...) * time.Millisecond to build the delay.", "Print a message before and after each sleep.", "Wait for all goroutines to finish before exiting." ]

Take Quiz

Keep learning

Related questions

View more