| Tested on | RHEL 10.2 with Go 1.27.0 |
|---|---|
| Package | time (Go 1.27.0 standard library) |
| Applies to | Any host with Go installed |
| Privilege | Normal user |
| Scope | Format and parse time strings with reference layouts, RFC3339, DateOnly, TimeOnly, DateTime, zone offsets, Parse versus ParseInLocation, and common layout mistakes. Does not cover Duration, Add or Sub, or Timer or Ticker basics. |
| Related guides | Golang time Get current time in milliseconds Golang subtract time and duration Golang ticker Getting started with Go |
To format a calendar date in Go:
t.Format("2006-01-02")Go does not use YYYY-MM-DD pattern letters. Layouts are written using the reference instant Mon Jan 2 15:04:05 MST 2006, so the layout string looks like the output you want.
Think of the mapping as:
YYYY-MM-DD → 2006-01-02The digits are not arbitrary: 2006 is the reference year, 01 is the reference month, and 02 is the reference day. Search queries such as golang time format yyyy-mm-dd or Go date format usually mean this translation, not a different API.
Format turns a time.Time into text; Parse and ParseInLocation rebuild a time.Time from text. Both sides share the same layout rules. For time.Now, durations, and arithmetic without string conversion, start with Golang time.
How Go's reference-time layout works
Go layouts are example timestamps, not strftime tokens. If you want output shaped like 2026-08-23, you write 2006-01-02 because those digits are the reference calendar date January 2, 2006 in year-month-day order.
The full reference string is Mon Jan 2 15:04:05 MST 2006. You rarely need every piece in one layout. Pick the components that match the string shape your log line, filename, or API expects.
| Reference value | Meaning |
|---|---|
2006 |
Four-digit year |
06 |
Two-digit year |
01 |
Numeric month |
Jan |
Short month |
January |
Full month |
02 |
Zero-padded day |
2 |
Day without leading zero |
15 |
24-hour hour |
03 |
12-hour hour |
04 |
Minute |
05 |
Second |
PM |
AM/PM marker |
MST |
Zone abbreviation |
-07:00 |
Numeric offset with colon |
Anything in the layout that is not a layout word is copied literally: punctuation, spaces, and fixed words pass through unchanged.
The easy swap to avoid: 01 is the month and 04 is the minute.
Format date and time with Time.Format
Format renders one time.Time using a layout that mirrors the shape you need. The location on t affects the calendar and clock fields you read, so a time.Now().Format("2006-01-02") line follows your process local zone unless you call UTC() first.
Common golang time format yyyy-mm-dd searches map to one program with several layouts:
package main
import (
"fmt"
"time"
)
func main() {
t := time.Date(2026, time.August, 23, 15, 30, 45, 0, time.UTC)
fmt.Println(t.Format("2006-01-02"))
fmt.Println(t.Format("2006-01-02 15:04:05"))
fmt.Println(t.Format("02 Jan 2006"))
fmt.Println(t.Format("03:04 PM"))
}Save that program as main.go and run it from the module directory:
go run .On Go 1.27.0 the program prints four shaped strings for the same instant:
2026-08-23
2026-08-23 15:30:45
23 Aug 2026
03:30 PM| Layout | Typical output shape |
|---|---|
2006-01-02 |
2026-08-23 |
2006-01-02 15:04:05 |
2026-08-23 15:30:45 |
02 Jan 2006 |
23 Aug 2026 |
03:04 PM |
03:30 PM |
time.Now().Format(layout) uses the same layouts for the current clock reading in the process local location. For a stable API stamp, convert first:
time.Now().UTC().Format(time.RFC3339)That pattern is the usual answer for format current time in Go when downstream systems expect UTC.
Use built-in Go time layouts
When a named constant matches your contract, prefer it over hand-written digits. DateOnly, TimeOnly, and DateTime were added in Go 1.20 as stable names for the most common shapes, so you do not need to memorize the digit string for every log format.
t.Format(time.DateOnly)
t.Format(time.TimeOnly)
t.Format(time.DateTime)
t.Format(time.RFC3339)
t.Format(time.RFC3339Nano)| Constant | Typical layout |
|---|---|
time.DateOnly |
2006-01-02 |
time.TimeOnly |
15:04:05 |
time.DateTime |
2006-01-02 15:04:05 |
time.RFC3339 |
Internet and API timestamps |
time.RFC3339Nano |
RFC3339 with fractional seconds |
On Go 1.27.0, time.Date(2026, time.August, 23, 15, 30, 45, 0, time.UTC) formatted with time.RFC3339 prints 2026-08-23T15:30:45Z. Use RFC3339Nano when fractional seconds must round-trip through JSON or protobuf timestamps.
Format UTC and timezone offsets
Convert to UTC before formatting when the contract expects a Z suffix:
t.UTC().Format(time.RFC3339)Zone pieces in a layout control how the suffix is rendered. They describe the output shape, not a separate formatting step:
MSTrequests a zone abbreviation such asISTorUTC.-07:00requests a signed numeric offset with a colon, such as+05:30.Z07:00printsZfor UTC and a numeric offset otherwise.
Abbreviations can be ambiguous across databases. When an exact offset matters for compliance or billing, prefer -07:00 or Z07:00 over MST alone.
ist := time.FixedZone("IST", 5*3600+30*60)
t := time.Date(2026, 8, 23, 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 ends with +05:30 for the IST wall time. The second line ends with Z for the same instant in UTC. Combine Z07:00 with the rest of an ISO-style layout when you need RFC-like output without memorizing the full constant string.
Parse date and time strings with time.Parse
Parse requires a layout that mirrors the input string exactly, including punctuation and field order. The same reference digits apply on the parse side: if you formatted with 2006-01-02, parse with 2006-01-02.
t, err := time.Parse("2006-01-02", "2026-08-23")
if err != nil {
return err
}When the parsed text contains no time-zone information, time.Parse interprets it as UTC. A date-only parse of "2026-08-23" therefore yields midnight UTC on that calendar day.
For strings that already include a zone, match the layout to the input. time.Parse(time.RFC3339, "2026-08-23T15:30:45Z") reads the offset or Z suffix from the text itself.
Always check err. A mismatch in separators, field width, or order returns a parse error rather than a partial value.
Parse local wall time with time.ParseInLocation
Use ParseInLocation when zone-less text represents wall time in a specific region. That is the main difference from Parse: both need matching layouts, but the location argument tells Go which zone the wall clock belongs to.
loc, err := time.LoadLocation("Asia/Kolkata")
if err != nil {
return err
}
t, err := time.ParseInLocation(
"2006-01-02 15:04",
"2026-08-23 15:30",
loc,
)
if err != nil {
return err
}The string 2026-08-23 15:30 carries no offset, so Parse would treat it as UTC wall time. ParseInLocation attaches the Asia/Kolkata zone instead. On Go 1.27.0 that parse formats as 2026-08-23T15:30:00+05:30.
Do not append " IST" or other zone labels to arbitrary timestamp strings to fake a zone. Load a Location and parse with ParseInLocation, or include an offset in the layout and input when the contract requires it. For fixed offsets without DST, time.FixedZone is enough when you already know the offset in seconds.
Common time-format mistakes
| Mistake | Correct approach |
|---|---|
YYYY-MM-DD in a layout |
2006-01-02 |
Using 04 for month |
04 means minute; month is 01 |
Using 01 for minute |
01 means month; minute is 04 |
| Layout does not match input shape | Make the layout mirror the input bytes |
Formatting local time then adding Z manually |
Convert to UTC and use RFC3339 |
| Zone abbreviation when exact offset matters | Prefer numeric offset or RFC3339 |
Parsing local wall time with Parse |
Use ParseInLocation |
A layout of "YYYY-MM-DD" does not produce a date. Go copies unrecognized letters literally, so t.Format("YYYY-MM-DD") prints the text YYYY-MM-DD. Use 2006-01-02 for a real calendar date.
Parsing "18-06-2026" with layout 2006-01-02 also fails because the field order and separators do not match. Swap the layout to 02-01-2006 when the input uses day-month-year ordering.
References
Summary
Golang time format searches usually boil down to one translation: YYYY-MM-DD in other ecosystems becomes 2006-01-02 in Go because layouts use the reference instant Mon Jan 2 15:04:05 MST 2006.
Call t.Format with reference digits or named constants such as time.DateOnly, time.DateTime, and time.RFC3339. Use MST, -07:00, and Z07:00 when the suffix must show an abbreviation or offset. Z07:00 is the layout fragment people reach for when they want Z in UTC and a numeric offset everywhere else.
Parse with a layout that matches the input shape byte for byte. Use Parse when zone-less text should mean UTC, and ParseInLocation when zone-less text is local wall time in a known region.
Remember that 01 is month and 04 is minute, check parse errors, and lean on RFC3339 when APIs expect Internet timestamps. For clocks, durations, and scheduling outside string conversion, return to Golang time.

