Dates and Times

The time package handles instants (time.Time), lengths of time (time.Duration), time zones, and formatting and parsing. Its most distinctive feature is how layouts are written: instead of codes like %Y-%m-%d, you write a sample date — always the same reference moment — the way you want it to look. This lesson covers creating, formatting, parsing, comparing and calculating with times.

Creating times

time.Now() gives the current time; time.Date builds a specific one. (This lesson uses fixed dates so the output is the same whenever you run it.)

GO
package main

import (
	"fmt"
	"time"
)

func main() {
	launch := time.Date(2026, time.October, 8, 14, 30, 0, 0, time.UTC)
	fmt.Println(launch)
	fmt.Println(launch.Year(), launch.Month(), launch.Day(), launch.Weekday())
	fmt.Println(launch.Hour(), launch.Minute(), launch.YearDay())

	now := time.Now()
	fmt.Println(now.After(launch.AddDate(-10, 0, 0))) // true, unless your clock is very wrong
}
Output
2026-10-08 14:30:00 +0000 UTC
2026 October 8 Thursday
14 30 281
true

time.Date normalises out-of-range values, so October 32 becomes November 1 — handy for date arithmetic, but it means mistakes don't cause errors.

Formatting with the reference time

Layouts are written using the reference time Monday, January 2, 15:04:05, 2006, in time zone MST (UTC−7). It's easy to remember as 1 2 3 4 5 6 7: month 1, day 2, hour 3 (15), minute 4, second 5, year 6, zone −7. Write those numbers in the shape you want:

GO
package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Date(2026, 3, 7, 9, 5, 2, 0, time.UTC)

	fmt.Println(t.Format("2006-01-02"))
	fmt.Println(t.Format("02/01/2006 15:04"))
	fmt.Println(t.Format("Jan 2, 2006 at 3:04pm"))
	fmt.Println(t.Format("Monday, 2 January 2006"))
	fmt.Println(t.Format(time.RFC3339))
	fmt.Println(t.Format(time.Kitchen), t.Format(time.DateOnly), t.Format(time.TimeOnly))
}
Output
2026-03-07
07/03/2026 09:05
Mar 7, 2026 at 9:05am
Saturday, 7 March 2026
2026-03-07T09:05:02Z
9:05AM 2026-03-07 09:05:02
ElementLayoutElementLayout
Year2006, 06Hour15 (24h), 3, 03 (12h)
Month01, 1, Jan, JanuaryMinute, second04, 05
Day02, 2, _2AM/PMPM, pm
WeekdayMon, MondayFraction of second.000, .999
ZoneMST, -0700, Z07:00Constantstime.RFC3339, time.DateTime, …

A common mistake is writing "2006-02-01" and getting day and month swapped, or "YYYY-MM-DD", which prints literally. Use the constants (time.DateOnly, time.RFC3339) when they fit.

Parsing

time.Parse uses the same kind of layout to read text. Without zone information in the text, the result is in UTC; ParseInLocation lets you say which zone the text is in:

GO
package main

import (
	"fmt"
	"time"
)

func main() {
	t, err := time.Parse("2006-01-02 15:04", "2026-12-24 18:00")
	fmt.Println(t, err)

	t2, err := time.Parse(time.RFC3339, "2026-07-01T08:00:00+02:00")
	fmt.Println(t2, t2.UTC())

	_, err = time.Parse(time.DateOnly, "2026-02-30")
	fmt.Println(err)

	lisbon, _ := time.LoadLocation("Europe/Lisbon")
	t3, _ := time.ParseInLocation("02/01/2006 15:04", "15/08/2026 10:00", lisbon)
	fmt.Println(t3)
}
Output
2026-12-24 18:00:00 +0000 UTC <nil>
2026-07-01 08:00:00 +0200 +0200 2026-07-01 06:00:00 +0000 UTC
parsing time "2026-02-30": day out of range
2026-08-15 10:00:00 +0100 WEST

Durations

A time.Duration is a number of nanoseconds, with constants such as time.Second and time.Hour to build them. Subtracting two times gives a duration:

GO
package main

import (
	"fmt"
	"time"
)

func main() {
	d := 90 * time.Minute
	fmt.Println(d, d.Hours(), d.Minutes())

	timeout := 2*time.Second + 500*time.Millisecond
	fmt.Println(timeout, timeout.Milliseconds())

	start := time.Date(2026, 10, 8, 9, 0, 0, 0, time.UTC)
	end := time.Date(2026, 10, 9, 17, 45, 30, 0, time.UTC)
	elapsed := end.Sub(start)
	fmt.Println(elapsed, elapsed.Round(time.Hour), elapsed.Truncate(time.Hour))

	p, err := time.ParseDuration("1h15m30s")
	fmt.Println(p, p.Seconds(), err)
}
Output
1h30m0s 1.5 90
2.5s 2500
32h45m30s 33h0m0s 32h0m0s
1h15m30s 4530 <nil>

Multiplying a duration by an integer variable needs a conversion: time.Duration(n) * time.Second.

Date arithmetic and comparison

GO
package main

import (
	"fmt"
	"time"
)

func main() {
	ordered := time.Date(2026, 1, 31, 12, 0, 0, 0, time.UTC)

	fmt.Println(ordered.Add(36 * time.Hour).Format(time.DateTime))
	fmt.Println(ordered.AddDate(0, 0, 14).Format(time.DateOnly)) // two weeks later
	fmt.Println(ordered.AddDate(0, 1, 0).Format(time.DateOnly))  // "one month later"

	due := ordered.AddDate(0, 0, 30)
	paid := time.Date(2026, 3, 5, 9, 0, 0, 0, time.UTC)
	fmt.Println(paid.After(due), paid.Before(due), paid.Equal(paid.UTC()))
	fmt.Printf("%.0f days late\n", paid.Sub(due).Hours()/24)
}
Output
2026-02-02 00:00:00
2026-02-14
2026-03-03
true false true
3 days late

Adding one month to January 31 gives March 3, because "February 31" is normalised. Calendar arithmetic is full of such edge cases; decide what your application should do and test it. Compare times with Before, After and Equal rather than ==, which also compares location and internal details.

Time zones

A time.Time is an instant plus a location used for display. In shows the same instant in another zone:

GO
package main

import (
	"fmt"
	"time"
)

func main() {
	meeting := time.Date(2026, 10, 8, 15, 0, 0, 0, time.UTC)
	for _, zone := range []string{"America/New_York", "Europe/Berlin", "Asia/Tokyo"} {
		loc, err := time.LoadLocation(zone)
		if err != nil {
			fmt.Println(err)
			continue
		}
		fmt.Printf("%-17s %s\n", zone, meeting.In(loc).Format("Mon 15:04 MST"))
	}
}
Output
America/New_York  Thu 11:00 EDT
Europe/Berlin     Thu 17:00 CEST
Asia/Tokyo        Fri 00:00 JST

Store and compute in UTC; convert to the user's zone only for display. LoadLocation reads the operating system's time-zone database; to embed one in your program (for minimal containers), import time/tzdata.

Unix timestamps

GO
package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
	fmt.Println(t.Unix(), t.UnixMilli())
	fmt.Println(time.Unix(1_800_000_000, 0).UTC())
}
Output
1767225600 1767225600000
2027-01-15 08:00:00 +0000 UTC

Measuring elapsed time

time.Since(start) measures how long something took. Times from time.Now() include a monotonic clock reading, so measurements stay correct even if the system clock is adjusted in between:

GO
package main

import (
	"fmt"
	"time"
)

func main() {
	start := time.Now()
	time.Sleep(30 * time.Millisecond)
	elapsed := time.Since(start)
	fmt.Println(elapsed >= 30*time.Millisecond, elapsed < time.Second)
}
Output
true true

Try it yourself

Calculate how many days are left until a deadline, and print the deadline in a friendly format.

Try it yourself · main.go
Edit the code, copy it into main.go in a new folder and run it with go run main.go (Go 1.27 or later). How to install Go
Output
84 days until Thursday, 31 December 2026
weeks: 12 and 0 days
Next: building HTTP servers.