time, timezones & context Integration
Go's time package models instants, durations, and recurring events.
Search across all documentation pages
Go's time package models instants, durations, and recurring events.
context attaches deadlines and cancellation to those operations so servers stop work when clients disconnect or budgets expire.
Use time.Time for calendar instants and time.Duration for elapsed intervals.
Monotonic clock readings prevent daylight-saving jumps from corrupting Since measurements.
Load locations with time.LoadLocation for correct local display; store UTC in databases.
Pair context.WithTimeout with blocking calls so goroutines exit promptly.
Quick-reference recipe card - copy-paste ready.
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
timer := time.NewTimer(500 * time.Millisecond)
defer timer.Stop()
select {
case <-timer.C:
// work
case <-ctx.Done():
return ctx.Err()
}When to reach for this:
time.Since and time.Until.time.Ticker.context.Context.package main
import (
"context"
"fmt"
"time"
)
func fetchWithDeadline(ctx context.Context, url string) error {
ctx, cancel := context.WithTimeout(ctx, 800*time.Millisecond)
defer cancel()
done := make(chan error, 1)
go func() {
// stand-in for net/http or database call
time.Sleep(300 * time.Millisecond)
done <- nil
}()
select {
case err := <-done:
return err
case <-ctx.Done():
return fmt.Errorf("fetch %s: %w", url, ctx.Err())
}
}
func nextBusinessOpen(now time.Time, loc *time.Location) time.Time {
local := now.In(loc)
// naive example: next 09:00 local on a weekday
open := time.Date(local.Year(), local.Month(), local.Day(), 9, 0, 0, 0, loc)
if !local.Before(open) {
open = open.Add(24 * time.Hour)
}
return open.UTC()
}
func main() {
loc, err := time.LoadLocation("America/New_York")
if err != nil {
panic(err)
}
if err := fetchWithDeadline(context.Background(), "https://example.com"); err != nil {
panic(err)
}
utcNow := time.Now().UTC()
fmt.Println("next open UTC:", nextBusinessOpen(utcNow, loc))
}What this demonstrates:
select bridging blocking work and cancellation.LoadLocation for IANA zone names and converting display instants to UTC.time.Now captures both wall clock and monotonic readings in one time.Time value.t2.Sub(t1)) uses monotonic readings when both times share the same monotonic base.time.Timer fires once; time.Ticker repeats until Stop.context.WithDeadline and WithTimeout schedule automatic cancel() at an instant derived from time.Time.| Type | Use for | Avoid for |
|---|---|---|
time.Time | Instants, serialization with RFC3339 | Storing bare Unix seconds without zone info |
time.Duration | Timeouts, backoff, metrics | Calendar arithmetic (use AddDate) |
time.Location | Regional display | Fixed time.FixedZone for DST regions |
time.Ticker | Periodic metrics flush | One-shot delays (use Timer) |
// Parse and format with explicit layouts (reference time Mon Jan 2 15:04:05 MST 2006):
t, _ := time.Parse(time.RFC3339, "2026-07-15T12:00:00Z")
// Sleep is not cancellable - prefer select:
select {
case <-time.After(delay):
case <-ctx.Done():
return ctx.Err()
}time.Sleep without context - Shutdown and client cancel cannot interrupt sleep. Fix: select on ctx.Done() and time.After.Location.timer.Stop() or ticker.Stop() - Leaks goroutines until fire time. Fix: defer Stop() immediately after creation.time.Time with == across zones - Same instant may differ in display fields. Fix: compare t1.Equal(t2) or convert both to UTC.time.After in tight loops - Creates a new timer per iteration. Fix: reuse time.NewTimer and reset carefully.context.Background() in servers - Ignores incoming request cancellation. Fix: accept ctx from r.Context() or parent scope.| Alternative | Use When | Don't Use When |
|---|---|---|
time.Tick | Quick demos only | Production code (cannot stop underlying ticker) |
cron libraries | Complex schedules | Simple fixed intervals suffice |
Unix millis int64 | Cross-language wire format | In-process duration math without conversion |
clock test fakes | Deterministic tests | Production scheduling |
A hidden reading inside time.Time used for duration measurements.
It ignores wall clock adjustments like NTP jumps when computing Since and Until.
Store UTC instants in databases, often as TIMESTAMPTZ or RFC3339 strings.
Use time.Time in Go code for zone and monotonic semantics.
time.LoadLocation("Europe/Berlin") using IANA names from the zoneinfo database shipped with Go.
Avoid hardcoding UTC+1 offsets for regions with DST.
A Timer sends one value on C after a delay.
A Ticker sends repeatedly until Stop.
Use tickers for heartbeat loops, timers for one-shot delays.
cancel releases the associated timer resources early if work finishes before the deadline.
It is required even when the timeout fires.
http.Server read/write timeouts cancel r.Context() when they fire.
Handlers should pass r.Context() downstream so database calls stop too.
ParseDuration supports ns through hours (h).
For days or months, use time.AddDate on time.Time, not duration strings.
The context's deadline passed before work completed.
Distinguish from context.Canceled when something explicitly called cancel().
time.Now returns local wall time with location set to Local.
Call .UTC() before persisting or comparing across machines.
Inject a small interface wrapping Now and After, or pass deadlines as parameters.
Avoid sleeping real time in unit tests.
time.Tick cannot be stopped, leaking a goroutine for the process lifetime.
Use time.NewTicker and Stop instead.
A select with default may busy-spin if you poll ticker.C without blocking.
Structure loops to block on ticker.C or combine with ctx.Done().
time.Since examplesStack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026