Formatting and Parsing Time in Go
Why Go formats dates with 2006-01-02 15:04:05, the layout constants, parsing strings into time.Time, and the common layout mistakes.
Every other language formats dates with codes like YYYY-MM-DD or %Y-%m-%d. Go uses an example date instead, and almost everyone is confused the first time. Once you know the one date to remember, formatting and parsing become simple.
The Reference Date
Go layouts are written using this exact moment:
Mon Jan 2 15:04:05 MST 2006
Read it as the numbers 1 2 3 4 5 6 7: month 01, day 02, hour 03 (or 15 in 24-hour time), minute 04, second 05, year 06 (or 2006), and time zone -0700. You write the layout by showing how that moment should look:
package main
import (
"fmt"
"time"
)
func main() {
t := time.Date(2026, time.March, 9, 14, 5, 30, 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:04 PM"))
fmt.Println(t.Format("Monday, 02 January 2006"))
fmt.Println(t.Format(time.RFC3339))
fmt.Println(t.Format(time.DateTime))
}
2026-03-09
09/03/2026 14:05
Mar 9, 2026 at 2:05 PM
Monday, 09 March 2026
2026-03-09T14:05:30Z
2026-03-09 14:05:30
02 gives a day with a leading zero, 2 gives it without. Jan is the short month name and January the full one. PM turns on 12-hour time with AM or PM.
Layout Constants
The time package has ready-made layouts so you do not have to remember them:
| Constant | Layout |
|---|---|
time.RFC3339 | 2006-01-02T15:04:05Z07:00 |
time.DateTime | 2006-01-02 15:04:05 |
time.DateOnly | 2006-01-02 |
time.TimeOnly | 15:04:05 |
time.Kitchen | 3:04PM |
Use time.RFC3339 for anything sent over an API or stored as text. It includes the time zone and sorts correctly as a string.
Parsing a String into time.Time
time.Parse takes the layout first and the string second:
package main
import (
"fmt"
"time"
)
func main() {
t, err := time.Parse(time.DateOnly, "2026-08-16")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(t, "|", t.Weekday())
_, err = time.Parse(time.DateOnly, "16-08-2026")
fmt.Println("error:", err)
}
2026-08-16 00:00:00 +0000 UTC | Sunday
error: parsing time "16-08-2026" as "2006-01-02": cannot parse "16-08-2026" as "2006"
The error shows the input and the layout, which helps when the format of the data is not what you expected.
Parse assumes UTC:
A string without zone information, like "2026-08-16 09:00", is read as UTC by time.Parse. If it is a local time, use time.ParseInLocation with the right location. The next page covers locations.
Common Layout Mistakes
package main
import (
"fmt"
"time"
)
func main() {
t := time.Date(2026, time.March, 9, 14, 5, 30, 0, time.UTC)
fmt.Println(t.Format("YYYY-MM-DD")) // codes from other languages
fmt.Println(t.Format("2006-01-02 15:01")) // 01 is month, not minute
fmt.Println(t.Format("2006-01-02 15:04")) // correct
}
YYYY-MM-DD
2026-03-09 14:03
2026-03-09 14:05
None of these produce an error. YYYY is not special, so it is printed as is. And 01 always means month, so 15:01 prints the month where the minutes should be. A third classic is swapping day and month as 2006-02-01. go vet catches that one with 2006-02-01 should be 2006-01-02, another reason to run it. If a formatted time looks almost right, compare your layout against 2006-01-02 15:04:05 digit by digit.
Fractional seconds:
Add .000 for milliseconds or .000000000 for nanoseconds after the seconds, for example 15:04:05.000. With .999 instead, trailing zeros are dropped.
You can read any layout:
If you can look at 02 Jan 06 15:04 and see "day, short month, two digit year, 24-hour time", the reference date has clicked.
Summary
Go layouts show how the reference time Mon Jan 2 15:04:05 MST 2006 should look, which is 1 2 3 4 5 6 7 in order. Use constants like time.RFC3339 and time.DateOnly when they fit. time.Parse takes the layout first and treats zoneless strings as UTC. Wrong layouts do not error, so check output carefully.