Format turns a time.Time into a string; Parse and ParseInLocation do the reverse. Both use the same idea: the layout is not YYYY-MM-DD—it is spelled with Go’s reference instant so the string you write looks like the string you want out. For clocks, durations, and time.Now without formatting detail, start with Golang time basics.
Tested on: Go 1.22 on 64-bit Linux; snippets were run with
go runwhile this article was revised.
Quick answer: use the reference date, not YYYY-MM-DD
Call t.Format(layout). For a calendar date like 2026-06-18, the usual layout is 2006-01-02 (year 2006, month 01, day 02). For a full stamp, combine pieces from the reference time Mon Jan 2 15:04:05 MST 2006.
package main
import (
"fmt"
"time"
)
func main() {
t := time.Date(2026, 6, 18, 14, 30, 0, 0, time.UTC)
fmt.Println(t.Format("2006-01-02"))
fmt.Println(t.Format("2006-01-02 15:04:05"))
fmt.Println(t.Format(time.RFC3339))
}You should see 2026-06-18, a space-separated date and time on that date, and an RFC3339 string with a Z or offset.
How time formatting works in Go
Go uses a reference time, not YYYY-MM-DD tokens
Other ecosystems often use pattern letters (YYYY, MM). Go instead writes an example timestamp: if you want 2026-06-18, you literally write 2006-01-02 because those digits are the reference calendar date January 2, 2006. Anything in the layout that is not a layout word is copied unchanged (punctuation, literal words).
The reference time: Mon Jan 2 15:04:05 MST 2006
Memorize the components you need most:
| Piece in reference | Role in output |
|---|---|
2006 |
Four-digit year |
01 |
Month (numeric) |
02 |
Day of month |
15 |
Hour, 24-hour clock |
03 |
Hour, 12-hour clock |
04 |
Minute |
05 |
Second |
PM |
AM or PM marker |
Important: 01 is the month and 04 is the minute—easy to swap by mistake.
Format current time in Go
Format time.Now as date and time
Use separate calls when you need a stable log line versus a full timestamp; both use the same reference digits in the layout string.
Format current time in UTC
UTC() shifts the instant before formatting so the suffix uses Z or a predictable offset.
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println(time.Now().Format("2006-01-02"))
fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
fmt.Println(time.Now().UTC().Format(time.RFC3339))
}The first two lines follow your local zone; the UTC line ends with Z or an offset depending on the instant.
Common Go time format layouts
Date-only layouts
These layouts omit clock fields entirely. For example, t.Format("2006-01-02") prints only the calendar part for t’s location (same digits as in the table below).
Time-only layouts
These layouts omit the calendar portion; combine them with date layouts when you need both. Examples: t.Format("15:04:05") for 24-hour clock, or t.Format("03:04 PM") for 12-hour with an AM/PM marker.
Date and time layouts
Use a space or T separator to mirror how your downstream system expects the string. Examples: t.Format("2006-01-02 15:04:05") or t.Format("2006-01-02T15:04:05Z07:00") when the suffix must follow RFC-style zone rules.
RFC3339 and ISO-like layouts
Prefer time.RFC3339 or time.RFC3339Nano when you exchange timestamps with APIs; they already encode offset rules. Example: t.UTC().Format(time.RFC3339) for a Z or offset suffix without hand-writing the full layout string.
| Desired shape | Layout or constant |
|---|---|
2026-06-18 |
2006-01-02 |
18-06-2026 |
02-01-2006 |
18/06/2026 |
02/01/2006 |
Jun 18, 2026 |
Jan 2, 2006 |
June 18, 2026 |
January 2, 2006 |
14:30:00 |
15:04:05 |
14:30 |
15:04 |
02:30 PM |
03:04 PM |
2026-06-18 14:30:00 |
2006-01-02 15:04:05 |
2026-06-18T14:30:00Z |
time.RFC3339 or 2006-01-02T15:04:05Z07:00 |
2026-06-18T14:30:00.123Z |
2006-01-02T15:04:05.000Z07:00 or time.RFC3339Nano |
20260618143000 |
20060102150405 |
Thu, 18 Jun 2026 14:30:00 UTC |
Mon, 02 Jan 2006 15:04:05 MST |
Parse time from string in Go
The layout for Parse must mirror the input string exactly, including separators and field order. time.Parse uses UTC when the layout has no zone. When the text has no offset but is wall time in a city, use time.ParseInLocation with time.LoadLocation or a fixed zone.
Use time.Parse with matching layout
time.Parse(layout, value) returns an error when punctuation, field width, or order differs from the layout string. Example: time.Parse("02/01/2006", "18/06/2026") succeeds because the slashes and day/month/year order match the layout.
Parse date-only strings
A layout like 2006-01-02 only reads the calendar portion; the resulting Time is midnight in UTC unless you follow up with In or ParseInLocation. Example: time.Parse("2006-01-02", "2026-06-18") yields 2026-06-18 00:00:00 +0000 UTC.
Parse RFC3339 timestamps
time.RFC3339 matches strings with a T separator and a zone (Z or ±hh:mm); use RFC3339Nano when fractional seconds appear. Example: time.Parse(time.RFC3339, "2026-06-18T14:30:00Z") reads the instant in UTC.
Use time.ParseInLocation for local time
When the input has no zone suffix but represents local wall time (for example a form field in Asia/Kolkata), parse with ParseInLocation and a loaded *time.Location. Example: time.ParseInLocation("2006-01-02 15:04:05", "2026-06-18 09:00:00", loc) with loc from time.LoadLocation("Asia/Kolkata") or time.FixedZone(...).
package main
import (
"fmt"
"time"
)
func main() {
layout := "2006-01-02"
t, err := time.Parse(layout, "2026-06-18")
if err != nil {
panic(err)
}
fmt.Println(t.UTC())
rfc := "2026-06-18T14:30:00Z"
t2, err := time.Parse(time.RFC3339, rfc)
if err != nil {
panic(err)
}
fmt.Println(t2)
loc := time.FixedZone("IST", 5*3600+30*60)
t3, err := time.ParseInLocation("2006-01-02 15:04:05", "2026-06-18 09:00:00", loc)
if err != nil {
panic(err)
}
fmt.Println(t3)
}You get a midnight UTC parse for the date-only string, the RFC3339 instant in UTC, and the wall time interpreted in the fixed IST offset.
Format and parse time zones
| Layout fragment | Meaning | Example |
|---|---|---|
MST |
Zone abbreviation | UTC, IST |
-0700 |
Offset, no colon | +0530 |
-07:00 |
Offset with colon | +05:30 |
Z0700 |
Z if UTC else compact offset |
Z or +0530 |
Z07:00 |
Z if UTC else offset with colon |
Z or +05:30 |
Z07:00 is what people mean when they search for RFC-style output: UTC prints as Z, other locations print a signed offset. Combine with the rest of the layout, for example 2006-01-02T15:04:05Z07:00.
package main
import (
"fmt"
"time"
)
func main() {
ist := time.FixedZone("IST", 5*3600+30*60)
t := time.Date(2026, 6, 18, 12, 0, 0, 0, ist)
fmt.Println(t.Format("2006-01-02T15:04:05Z07:00"))
fmt.Println(t.UTC().Format("2006-01-02T15:04:05Z07:00"))
}The first line shows a +05:30 style suffix; the second shows Z for the same instant in UTC.
Go layout symbols explained
| Meaning | Layout token |
|---|---|
| Four-digit year | 2006 |
| Two-digit year | 06 |
| Numeric month | 01 |
| Short month name | Jan |
| Long month name | January |
| Day of month (zero-padded) | 02 |
| Day without leading zero | 2 |
| Short weekday | Mon |
| Long weekday | Monday |
| 24-hour hour | 15 |
| 12-hour hour | 03 |
| Minute | 04 |
| Second | 05 |
| AM / PM | PM |
| Zone name | MST |
| Numeric offset (colon) | -07:00 |
UTC as Z or offset |
Z07:00 |
| Fractional seconds (milliseconds) | .000 |
| Fractional seconds (trimmed) | .999 |
Common mistakes with Go time format
Using YYYY-MM-DD instead of 2006-01-02
Letters that are not part of the reference vocabulary are emitted literally, so "YYYY-MM-DD" prints those letters, not the date.
package main
import (
"fmt"
"time"
)
func main() {
t := time.Date(2026, 6, 18, 0, 0, 0, 0, time.UTC)
fmt.Println(t.Format("YYYY-MM-DD"))
fmt.Println(t.Format("2006-01-02"))
}The first line is the literal YYYY-MM-DD; the second is the real calendar date.
Using 04 for month or 01 for minute
01 is always month and 04 is always minute in layouts.
Layout does not match the input string
package main
import (
"fmt"
"time"
)
func main() {
_, err := time.Parse("2006-01-02", "18-06-2026")
fmt.Println(err)
}Parse returns an error because the hyphens and field order do not match; switch the layout to 02-01-2006 for that input.
Confusing UTC, local time, and offset
Use UTC(), Local(), and ParseInLocation deliberately. Store UTC in systems; render local time at the edge.
Rebuilding timestamps with fmt instead of Format
Prefer Format so zone, padding, and fractional seconds stay consistent.
Go time format cheat sheet
| Goal | Pattern |
|---|---|
| Date only | time.Now().Format("2006-01-02") |
| Date and time | time.Now().Format("2006-01-02 15:04:05") |
| UTC API stamp | time.Now().UTC().Format(time.RFC3339) |
| RFC3339 with nanos | t.Format(time.RFC3339Nano) |
| ISO-style with Z rule | "2006-01-02T15:04:05Z07:00" |
| Parse date | time.Parse("2006-01-02", value) |
| Parse with zone | time.ParseInLocation(layout, value, loc) |
Which layout should you use?
- Interchange and JSON-style APIs:
time.RFC3339orRFC3339Nano. - Human logs in one time zone:
2006-01-02 15:04:05plus an explicit zone policy. - Filename-safe:
20060102150405.
Summary
Go time format and golang time parse both revolve around the reference time: build layouts from 2006-01-02 15:04:05 and friends, never from YYYY-MM-DD. Use t.Format for output, time.Parse when UTC is correct for zoneless strings, and ParseInLocation for regional wall time. Z07:00 gives RFC-style Z in UTC and numeric offsets elsewhere. Match layouts to inputs byte-for-byte, remember 01 is month and 04 is minute, and lean on named constants like time.RFC3339 when they fit your contract.

