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.)
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 }
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:
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)) }
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
| Element | Layout | Element | Layout |
|---|---|---|---|
| Year | 2006, 06 | Hour | 15 (24h), 3, 03 (12h) |
| Month | 01, 1, Jan, January | Minute, second | 04, 05 |
| Day | 02, 2, _2 | AM/PM | PM, pm |
| Weekday | Mon, Monday | Fraction of second | .000, .999 |
| Zone | MST, -0700, Z07:00 | Constants | time.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:
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) }
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:
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) }
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
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) }
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:
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")) } }
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
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()) }
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:
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) }
true true
Try it yourself
Calculate how many days are left until a deadline, and print the deadline in a friendly format.
84 days until Thursday, 31 December 2026 weeks: 12 and 0 days