time

Guided tour · Time & Context · pkg.go.dev →

Time, durations, timers, tickers, parsing and formatting. The single source of truth for 'now'.

Wall-clock time, monotonic durations, sleeps, timers, and parsing/formatting using a reference layout.

Now
t := time.Now()
Sleep
time.Sleep(2 * time.Second)
How long since
elapsed := time.Since(start)
Add / subtract
t.Add(24 * time.Hour)
Format ISO-8601 / RFC3339
t.Format(time.RFC3339)
Parse a date
t, err := time.Parse(time.RFC3339, "2026-04-26T10:00:00Z")
Periodic ticker
tk := time.NewTicker(time.Second)
defer tk.Stop()
for now := range tk.C { use(now) }
One-shot timer
time.AfterFunc(d, func() { ... })

Now, durations, arithmetic

time.Now and monotonic clock

Now() embeds both a wall-clock reading and a monotonic reading. Subtracting two Times from the same process uses the monotonic part — immune to clock changes.

now := time.Now()
fmt.Println(now)
fmt.Println(now.Unix())          // seconds since 1970
fmt.Println(now.UnixMilli())     // ms since 1970

Duration constants

time.Duration is an int64 of nanoseconds. The constants (time.Second, time.Millisecond, ...) let you express durations readably.

d := 2*time.Hour + 30*time.Minute
fmt.Println(d)                    // "2h30m0s"
fmt.Println(d.Minutes())          // 150
fmt.Println(time.Minute / time.Second) // 60
Output
2h30m0s
150
60

Add, Sub, Before/After/Equal

now := time.Now()
later := now.Add(1 * time.Hour)
diff := later.Sub(now)               // 1h0m0s
fmt.Println(later.After(now))        // true
fmt.Println(diff)

Since and Until — shortcuts for common cases

time.Since(t) == time.Now().Sub(t). time.Until(t) == t.Sub(time.Now()).

start := time.Now()
expensiveWork()
fmt.Println("took", time.Since(start))

Formatting and parsing

Go uses a reference date Mon Jan 2 15:04:05 MST 2006 (01/02 03:04:05PM '06 -0700) instead of format directives. Memorize it once.

Format with a layout — reference date

now := time.Date(2024, 3, 14, 15, 9, 26, 0, time.UTC)
fmt.Println(now.Format("2006-01-02 15:04:05"))
fmt.Println(now.Format(time.RFC3339))
Output
2024-03-14 15:09:26
2024-03-14T15:09:26Z

Parse — must match the layout exactly

t, err := time.Parse("2006-01-02", "2024-03-14")
fmt.Println(t, err)
Output
2024-03-14 00:00:00 +0000 UTC <nil>

Common built-in layouts

// time.RFC3339 = "2006-01-02T15:04:05Z07:00"
// time.DateOnly = "2006-01-02"
// time.TimeOnly = "15:04:05"
// time.DateTime = "2006-01-02 15:04:05"
// time.Kitchen  = "3:04PM"

Timers, tickers, Sleep

time.Sleep — block for a duration

time.Sleep(200 * time.Millisecond)

time.After — one-shot channel fire

Useful in selects for timeouts. For cancellable timeouts, prefer context.WithTimeout.

select {
case msg := <-ch:
    fmt.Println("got", msg)
case <-time.After(2 * time.Second):
    fmt.Println("timeout")
}

time.NewTimer vs time.NewTicker

Timer fires once on C. Ticker fires repeatedly on C until Stop. Always Stop tickers you're done with or they leak goroutines.

t := time.NewTicker(500 * time.Millisecond)
defer t.Stop()
for i := 0; i < 3; i++ {
    <-t.C
    fmt.Println("tick", i)
}

AfterFunc — run a callback later

timer := time.AfterFunc(1*time.Second, func() {
    fmt.Println("fired")
})
// timer.Stop() would cancel before it fires

Zones and clocks

In a specific time zone

loc, _ := time.LoadLocation("Europe/London")
t := time.Now().In(loc)
fmt.Println(t.Format(time.RFC3339))

Truncate and Round — snap to intervals

t := time.Now()
fmt.Println(t.Truncate(time.Hour))  // start of the current hour
fmt.Println(t.Round(time.Minute))