Go time Package

Everything you need to work with dates, times, durations, timers, and timezones — with real examples.

time.Time time.Duration time.Location time.Timer time.Ticker Formatting Parsing Arithmetic

Getting the Current Time

💡
time.Time is a struct representing an instant in time with nanosecond precision. It includes wall clock, monotonic clock reading, and timezone location.
Current time — wall clock & monotonic Basics
package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()              // wall + monotonic

    fmt.Println(now)                // 2026-06-23 14:30:05.123456789 +0000 UTC m=+0.000000001
    fmt.Println(now.Year())         // 2026
    fmt.Println(now.Month())        // June
    fmt.Println(now.Day())          // 23
    fmt.Println(now.Hour())         // 0-23: 14
    fmt.Println(now.Minute())       // 0-59: 30
    fmt.Println(now.Second())       // 0-59: 5
    fmt.Println(now.Nanosecond())   // 0-999999999: 123456789
    fmt.Println(now.Weekday())      // Tuesday
    fmt.Println(now.YearDay())      // 1-366: 174
}
Constructing a specific time time.Date
// time.Date(year, month, day, hour, min, sec, nsec, loc)
launch := time.Date(2026, time.July, 4, 9, 0, 0, 0, time.UTC)
fmt.Println(launch)

// Zero value — Jan 1, year 1, 00:00:00 UTC
var zero time.Time
fmt.Println(zero.IsZero()) // true
Output
2026-07-04 09:00:00 +0000 UTC
true
🖋

Formatting & Parsing

⚠️
Go uses a reference time as the format template — Mon Jan 2 15:04:05 MST 2006. This is not a placeholder; use these exact values to define your layout.
Format — custom layouts Format()
now := time.Now()

// Common patterns
fmt.Println(now.Format("2006-01-02"))
fmt.Println(now.Format("02/01/2006"))
fmt.Println(now.Format("Jan 2, 2006"))
fmt.Println(now.Format("3:04 PM"))
fmt.Println(now.Format("Mon, 02 Jan 2006 15:04:05 -0700"))

// Pre-defined layouts in the time package
fmt.Println(now.Format(time.RFC3339))
fmt.Println(now.Format(time.RFC822))
fmt.Println(now.Format(time.Kitchen))
Output
2026-06-23
23/06/2026
Jun 23, 2026
2:30 PM
Tue, 23 Jun 2026 14:30:05 +0000
2026-06-23T14:30:05Z
23 Jun 26 14:30 UTC
2:30PM
Parsing — string → time.Time time.Parse
// time.Parse(layout, value) — always UTC
t1, err := time.Parse("2006-01-02", "2026-06-23")
if err != nil {
    fmt.Println("error:", err)
}
fmt.Println(t1)

// time.ParseInLocation — respects local timezone
loc, _ := time.LoadLocation("America/New_York")
t2, _ := time.ParseInLocation("2006-01-02 15:04:05", "2026-06-23 09:00:00", loc)
fmt.Println(t2)

// RFC3339 is the most portable internet format
t3, _ := time.Parse(time.RFC3339, "2026-06-23T14:30:05Z")
fmt.Println(t3.Format(time.RFC3339))
Output
2026-06-23 00:00:00 +0000 UTC
2026-06-23 09:00:00 -0400 EDT
2026-06-23T14:30:05Z

Durations

ℹ️
time.Duration is an int64 representing nanoseconds. All duration constants — time.Second, time.Minute, etc. — are just multiples of time.Nanosecond.
Duration constants Literals
var (
    ns  = time.Nanosecond   // 1
    us  = time.Microsecond  // 1000
    ms  = time.Millisecond  // 1_000_000
    s   = time.Second       // 1e9
    m   = time.Minute       // 60e9
    h   = time.Hour         // 3600e9
)

d := 2*time.Hour + 30*time.Minute
fmt.Println(d) // 2h30m0s
Duration from string ParseDuration
d, _ := time.ParseDuration("1h30m")
fmt.Println(d) // 1h30m0s

d2, _ := time.ParseDuration("300ms")
fmt.Println(d2) // 300ms

d3, _ := time.ParseDuration("2h45m30s")
fmt.Println(d3.Hours())   // 2.758...
fmt.Println(d3.Minutes()) // 165.5
fmt.Println(d3.Seconds()) // 9930
Truncate & Round a duration Precision
d := 17*time.Minute + 45*time.Second

fmt.Println(d.Truncate(time.Minute)) // 17m0s  (floor)
fmt.Println(d.Round(time.Minute))    // 18m0s  (nearest)

Time Arithmetic

past
t1
now
t2
future
t3
Since(t1) = now − t1 ▶ Duration
Until(t3) = t3 − now ▶ Duration
Sub(t1) = t2 − t1 ▶ Duration
Add, Sub, Since, Until Arithmetic
now := time.Now()

// Add a duration to a time
tomorrow  := now.Add(24 * time.Hour)
nextWeek  := now.Add(7 * 24 * time.Hour)
yesterday := now.Add(-24 * time.Hour)

// AddDate(years, months, days) — calendar-aware
nextMonth := now.AddDate(0, 1, 0)
nextYear  := now.AddDate(1, 0, 0)

// Sub: time.Time − time.Time → time.Duration
elapsed := now.Sub(yesterday)
fmt.Println(elapsed) // 24h0m0s

// Since / Until — shortcuts using time.Now()
start := time.Now()
// ... do work ...
fmt.Println(time.Since(start)) // elapsed since start

deadline := now.Add(5 * time.Minute)
fmt.Println(time.Until(deadline)) // time remaining
Output
24h0m0s
4m59.999872042s
Benchmarking execution time Pattern
func benchmark(name string, fn func()) {
    start := time.Now()
    fn()
    elapsed := time.Since(start)
    fmt.Printf("%s took %v\n", name, elapsed)
}

benchmark("heavy task", func() {
    time.Sleep(50 * time.Millisecond)
})
Output
heavy task took 50.123456ms
🔍

Comparing Times

ℹ️
Never use == to compare time.Time values — it also compares the monotonic clock and location pointer. Use .Equal() for semantic equality.
Before, After, Equal Comparison
t1 := time.Date(2026, time.January, 1, 0, 0, 0, 0, time.UTC)
t2 := time.Date(2026, time.June, 23, 0, 0, 0, 0, time.UTC)
t3 := t1

fmt.Println(t1.Before(t2)) // true  — t1 is earlier
fmt.Println(t2.After(t1))  // true  — t2 is later
fmt.Println(t1.Equal(t3))  // true  — same instant
fmt.Println(t1 == t3)       // may be false! don't use ==

// Clamp: check if time is within a range
func inRange(t, lo, hi time.Time) bool {
    return !t.Before(lo) && !t.After(hi)
}
Output
true
true
true
true
🌍

Timezones

Loading and converting locations Timezones
now := time.Now().UTC() // start in UTC

// Load IANA timezone (requires tzdata on system or embed)
ny, _  := time.LoadLocation("America/New_York")
lon, _ := time.LoadLocation("Europe/London")
tok, _ := time.LoadLocation("Asia/Tokyo")

// Convert — same instant, different display
fmt.Println(now.In(ny))
fmt.Println(now.In(lon))
fmt.Println(now.In(tok))

// Fixed offset (when IANA name is unknown)
ist := time.FixedZone("IST", 5*3600+30*60) // +05:30
fmt.Println(now.In(ist))

// Get timezone name + offset
name, offset := now.In(ny).Zone()
fmt.Printf("%s  offset=%d\n", name, offset)
Output
2026-06-23 10:30:05 -0400 EDT
2026-06-23 15:30:05 +0100 BST
2026-06-24 00:30:05 +0900 JST
2026-06-23 21:00:05 +0530 IST
EDT  offset=-14400
Embed tzdata (no system dependency) Production tip
// In any .go file (often main.go or a dedicated tz.go)
import _ "time/tzdata"

// This embeds the full IANA timezone database into your binary,
// so LoadLocation works even in scratch containers with no /usr/share/zoneinfo
🔢

Unix Timestamps

Converting to/from Unix time Interop
now := time.Now()

// time.Time → Unix timestamp
fmt.Println(now.Unix())      // seconds since epoch
fmt.Println(now.UnixMilli()) // milliseconds
fmt.Println(now.UnixMicro()) // microseconds
fmt.Println(now.UnixNano())  // nanoseconds

// Unix timestamp → time.Time
ts := time.Unix(1750000000, 0)  // (sec, nsec)
fmt.Println(ts)

tsMs := time.UnixMilli(1750000000000)
fmt.Println(tsMs)

// JSON marshaling uses RFC3339 by default
type Event struct {
    At time.Time `json:"at"`
}
Output
1750686605
1750686605123
1750686605123456
1750686605123456789
2025-06-15 14:13:20 +0000 UTC
2025-06-15 14:13:20 +0000 UTC
😴

Sleep & Timing

time.Sleep — pause execution Sleep
// Pause the current goroutine
time.Sleep(500 * time.Millisecond)
time.Sleep(2 * time.Second)
time.Sleep(1 * time.Minute)

// Context-aware sleep (can be cancelled)
func sleepCtx(ctx context.Context, d time.Duration) error {
    select {
    case <-time.After(d):
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}

Timers

ℹ️
A Timer fires once after the duration. Always stop a timer you no longer need to free resources, and drain its channel when resetting.
One-shot timer time.NewTimer
timer := time.NewTimer(2 * time.Second)
defer timer.Stop() // always clean up

select {
case t := <-timer.C:
    fmt.Println("Timer fired at", t)
case <-time.After(1 * time.Second):
    fmt.Println("Timed out waiting for timer")
}

// time.AfterFunc — callback in new goroutine
time.AfterFunc(500*time.Millisecond, func() {
    fmt.Println("callback fired")
})
Resetting a timer safely Pattern
timer := time.NewTimer(5 * time.Second)

// Safe reset pattern
if !timer.Stop() {
    select {
    case <-timer.C: // drain if already fired
    default:
    }
}
timer.Reset(10 * time.Second)
🔁

Tickers

💡
A Ticker fires repeatedly at a fixed interval. Always call ticker.Stop() when done — unlike a channel close, it won't stop on its own.
Periodic work with a Ticker time.NewTicker
ticker := time.NewTicker(1 * time.Second)
done   := make(chan struct{})

go func() {
    for {
        select {
        case <-done:
            return
        case t := <-ticker.C:
            fmt.Println("tick at", t.Format(time.Kitchen))
        }
    }
}()

time.Sleep(5 * time.Second)
ticker.Stop()
close(done)
fmt.Println("ticker stopped")
Output
tick at 2:30PM
tick at 2:30PM
tick at 2:30PM
tick at 2:30PM
tick at 2:30PM
ticker stopped
Heartbeat / retry with ticker + context Real-world pattern
func pollUntilDone(ctx context.Context, interval time.Duration) {
    ticker := time.NewTicker(interval)
    defer ticker.Stop()

    for {
        select {
        case <-ctx.Done():
            fmt.Println("context cancelled, stopping poll")
            return
        case <-ticker.C:
            if checkHealth() {
                fmt.Println("healthy!")
                return
            }
            fmt.Println("still waiting...")
        }
    }
}
📋

Quick Reference

Function / Method Returns Description
time.Now()time.TimeCurrent local time (wall + monotonic)
time.Date(y,m,d,h,min,s,ns,loc)time.TimeConstruct a specific time
t.Format(layout)stringFormat time using reference layout
time.Parse(layout, s)time.Time, errorParse a string into time.Time
time.ParseInLocation(layout, s, loc)time.Time, errorParse respecting a timezone
t.Add(d)time.TimeAdd a duration to a time
t.AddDate(y,m,d)time.TimeCalendar-aware date arithmetic
t.Sub(u)time.DurationDifference between two times
time.Since(t)time.DurationElapsed time since t
time.Until(t)time.DurationTime remaining until t
t.Before(u)boolt is earlier than u
t.After(u)boolt is later than u
t.Equal(u)boolSame instant (timezone-agnostic)
t.In(loc)time.TimeConvert time to different timezone
t.UTC()time.TimeConvert to UTC
t.Local()time.TimeConvert to local time
t.Unix()int64Seconds since Unix epoch
t.UnixMilli()int64Milliseconds since Unix epoch
t.UnixNano()int64Nanoseconds since Unix epoch
time.Unix(sec, nsec)time.Timetime.Time from Unix timestamp
t.Truncate(d)time.TimeTruncate to multiple of d
t.Round(d)time.TimeRound to nearest multiple of d
t.IsZero()boolIs this the zero time?
t.Zone()string, intTimezone name and offset
time.Sleep(d)Pause goroutine for d
time.After(d)<-chan time.TimeChannel that sends after d
time.NewTimer(d)*time.TimerOne-shot timer
time.AfterFunc(d, f)*time.TimerCall f in goroutine after d
time.NewTicker(d)*time.TickerRepeating ticker every d
time.ParseDuration(s)time.Duration, errorParse "1h30m" → Duration
time.LoadLocation(name)*time.Location, errorLoad IANA timezone
time.FixedZone(name, offset)*time.LocationFixed UTC offset location