CHAPTER 28 · STANDARD LIBRARY

Time, Dates & Durations

The time package handles every time-related need: instants, durations, parsing, formatting, time zones, timers, and tickers. Two things make Go's design distinctive: time.Duration is a typed integer (nanoseconds) and date format strings use a reference time instead of cryptic codes like %Y-%m-%d.

Learning objectives

  • Get the current time and create specific instants.
  • Use time.Duration for arithmetic.
  • Format and parse times using the reference layout.
  • Convert between time zones.
  • Schedule one-shot timers and recurring tickers.

time.Time

now := time.Now()
fmt.Println(now)                      // 2026-04-22 11:35:00.123 +0000 UTC
fmt.Println(now.Year(), now.Month(), now.Day())

birthday := time.Date(1990, time.July, 4, 12, 0, 0, 0, time.UTC)
fmt.Println(birthday)

time.Duration

d := 2*time.Hour + 30*time.Minute
fmt.Println(d)                  // 2h30m0s
fmt.Println(d.Minutes())        // 150
fmt.Println(d.Seconds())        // 9000

Duration is just int64 nanoseconds. The constants (time.Hour, etc.) are typed for arithmetic: 5 * time.Second, 500 * time.Millisecond.

Layouts, the famous reference time

Instead of %Y-%m-%d, Go uses an example time:

// THE reference time: Mon Jan 2 15:04:05 MST 2006
// Memorize: 01 02 03 04 05 06 07  (month day hour minute second year week)
const layout = "2006-01-02 15:04:05"
fmt.Println(time.Now().Format(layout))    // 2026-04-22 11:35:00

Common predefined layouts:

time.RFC3339           // "2006-01-02T15:04:05Z07:00"
time.RFC1123           // "Mon, 02 Jan 2006 15:04:05 MST"
time.DateTime          // "2006-01-02 15:04:05"
time.DateOnly          // "2006-01-02"
time.TimeOnly          // "15:04:05"
!
Don't pick wrong magic numbers 2006 is the year, not 2007. 15 is 24-hour. 3 is 12-hour without leading zero. Get one digit wrong and your output is silently wrong. When in doubt, use the named constants (time.RFC3339 covers most needs).

Parsing & formatting

t, err := time.Parse("2006-01-02", "2025-12-25")
if err != nil { /* handle */ }
fmt.Println(t.Weekday())                 // Thursday

// With explicit time zone
loc, _ := time.LoadLocation("America/New_York")
t, _ = time.ParseInLocation("2006-01-02 15:04", "2025-12-25 09:30", loc)

Time arithmetic

now := time.Now()
later := now.Add(48 * time.Hour)         // 2 days from now
elapsed := time.Since(start)              // shortcut for Now().Sub(start)
fmt.Println(later.Before(now))            // false
fmt.Println(later.Equal(now.Add(48 * time.Hour)))  // true

Time zones

tokyo, _ := time.LoadLocation("Asia/Tokyo")
t := time.Now().In(tokyo)
fmt.Println(t)                  // same instant, displayed in Tokyo time

utc := time.Now().UTC()         // shortcut

Timers & tickers

// One-shot delay
<-time.After(2 * time.Second)
fmt.Println("two seconds later")

// Repeated tick
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for t := range ticker.C {
    fmt.Println("tick:", t)
    // break out via context, channel, etc.
}

Monotonic clocks

Every time.Now() result includes a monotonic component that's immune to wall-clock adjustments (NTP, manual changes). For measuring elapsed durations, this is automatic, you'll never see negative time.Since values from clock skew.

Check your understanding

Practice exercises

EXERCISE 1

Days until New Year

Write a program that prints the number of full days between today and the next January 1st.

Show solution
package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    nextYear := time.Date(now.Year()+1, time.January, 1, 0, 0, 0, 0, now.Location())
    days := int(time.Until(nextYear).Hours() / 24)
    fmt.Printf("%d days until New Year\n", days)
}

Further reading

Time tamed.