skip to content

Reference Layouts and Parsing

Go spells formats as an example timestamp, 2006-01-02 15:04:05, not as strftime codes, and time.Parse with no zone in the input hands you UTC rather than local time.

part ofGo (Golang)overview, primer and where to startread it →
on this pageshow

questions

5

Why does Go's time formatting use the layout string "2006-01-02" instead of a pattern like "yyyy-MM-dd"?

level: juniorimportance: must knowfreq 78%

answer

  1. Go shows you an example, not letters
  2. One fixed instant, retyped in your shape
  3. The numbers count 1 through 7
  4. 01 is month, 02 is day
  5. 15 is the odd one: the 24-hour hour

basics

~20 s

Go has no pattern letters. A layout is one fixed reference instant, Mon Jan 2 15:04:05 MST 2006, written in the shape you want output. So 2006 means year, 01 month, 02 day, 15 hour, 04 minute, 05 second.

solid answer

~40 s

Go's `time` package uses example-based layouts rather than a pattern language. Every layout is the single reference instant `Mon Jan 2 15:04:05 MST 2006` typed out in the format you want, and `Format` and `Parse` both read the same layout. The numbers are mnemonic: `01/02 03:04:05PM '06 -0700` runs 1 through 7 for month, day, hour, minute, second, year and zone offset. `15` is the 24-hour hour. Anything in the layout that is not a recognised reference element is copied through verbatim, which is why `Format("YYYY-MM-DD")` returns the literal text `YYYY-MM-DD` with no error at all. In practice you rarely hand-write layouts: constants such as `time.RFC3339`, `time.RFC1123`, `time.DateOnly` and `time.Kitchen` cover most cases.

code

go · 6 lines
go
t := time.Date(2024, time.March, 5, 14, 9, 7, 0, time.UTC)

t.Format("2006-01-02")        // "2024-03-05"
t.Format("02 Jan 2006 15:04") // "05 Mar 2024 14:09"
t.Format(time.RFC3339)        // "2024-03-05T14:09:07Z"
t.Format("YYYY-MM-DD")        // "YYYY-MM-DD" — not an error, just literal text

go deeper

for a junior

Memorise the reference instant Mon Jan 2 15:04:05 MST 2006 and be able to write a layout for a date you are shown. Know that 2006 is the year, 01 the month, 02 the day and 15 the 24-hour hour.

for a middle

Explain why Format never errors on a bad layout and what it does with unrecognised text, and cover the padding variants (2, 02, _2) and the fractional-second forms .000 versus .999.

for a senior

Show the review habit: prefer the named constants over hand-written layouts, and require a test that asserts the exact rendered string, because a wrong layout is silent in both directions.

for a principal

Own the convention. Decide which layout constant every service emits, and treat hand-rolled layouts in shared code as a defect to be replaced with a named constant or a small shared helper.

## The idea Most languages give you a *pattern language* for dates: `yyyy-MM-dd`, `%Y-%m-%d`, `dd/MM/yyyy`. You have to learn which letter means what, and whether the letter is case-sensitive (in many of them `MM` is month and `mm` is minute, which is a famous source of bugs). Go took a different route. There is exactly one **reference instant**: ``` Mon Jan 2 15:04:05 MST 2006 ``` A layout string is that instant written the way you want your output to look. If you want `2024-03-05`, you write the reference date in that shape: `2006-01-02`. If you want `05 Mar 2024 14:09`, you write `02 Jan 2006 15:04`. The layout is a worked example, not a grammar. ## The mnemonic The reference instant is chosen so the numeric elements run 1, 2, 3, 4, 5, 6, 7 in a natural order: ``` 01/02 03:04:05PM '06 -0700 ``` - `01` — month (January) - `02` — day of month - `03` — hour on a 12-hour clock - `04` — minute - `05` — second - `06` — two-digit year (`2006` for four digits) - `-0700` — zone offset (MST is UTC-7) The one that breaks the pattern is **`15`** — the hour on a 24-hour clock, so called because 15:04 is 3:04 PM. If your layout has `15` you do not add `PM`; if it has `03` you almost always must. ## The full element vocabulary - **Year**: `2006` (four digit), `06` (two digit). - **Month**: `1` (unpadded), `01` (zero padded), `Jan`, `January`. - **Day**: `2`, `02` (zero padded), `_2` (space padded). Day of year: `002`, `__2`. - **Weekday**: `Mon`, `Monday`. - **Hour**: `15` (24 hour), `3`, `03` (12 hour, needs `PM` or `pm`). - **Minute**: `4`, `04`. **Second**: `5`, `05`. - **Fractional seconds**: `.000` / `.000000` / `.000000000` keep trailing zeros; `.999` / `.999999` / `.999999999` drop them, and drop the decimal point entirely when the fraction is zero. - **Zone**: `MST` (abbreviation), `-0700`, `-07:00`, `-07`, and the `Z` forms `Z0700` / `Z07:00`, which print a literal `Z` when the time is UTC and a numeric offset otherwise. ## Format never fails `Time.Format(layout string) string` returns a string and no error. It walks the layout looking for reference elements and copies everything else — separators, spaces, and any text that happens not to be an element — straight through. That is a feature for punctuation (`"Mon, 02 Jan 2006"` works) and a trap for habits from other languages: `t.Format("YYYY-MM-DD")` compiles, runs, and returns the four characters `YYYY`, a hyphen, `MM`, a hyphen and `DD`, forever, on every input. There is no error to catch; the only defence is a test that asserts the rendered string. The same vocabulary drives parsing. `time.Parse(layout, value)` uses the layout to say what each piece of the input means, and it *does* return an error when the value does not fit. ## Use the constants The package ships the common layouts as constants, and reaching for them is both shorter and safer than retyping the reference instant: - `time.RFC3339` = `"2006-01-02T15:04:05Z07:00"` — the one to default to for machine interchange. - `time.RFC3339Nano` = `"2006-01-02T15:04:05.999999999Z07:00"` — same, with sub-second precision and trailing zeros trimmed. - `time.RFC1123` = `"Mon, 02 Jan 2006 15:04:05 MST"`, and `time.RFC1123Z` with a numeric offset instead of an abbreviation — the HTTP-ish shape. - `time.DateOnly` (`"2006-01-02"`), `time.DateTime` (`"2006-01-02 15:04:05"`), `time.TimeOnly` (`"15:04:05"`), `time.Kitchen` (`"3:04PM"`). ## Why this design The payoff is that a layout is self-documenting: you can read `"02 Jan 2006"` and know exactly what comes out, without consulting a table of letters, and there is no `MM`/`mm` hazard. The cost is that the numbers are arbitrary until you have memorised them, and a mistyped element is not a syntax error — it is either silently literal text on the format side, or a silently different field on the parse side. That second cost is where the classic `01` versus `02` bug lives: both are valid elements, so swapping them parses many real dates without complaint.

  • Walk me through what each number in the reference layout means.
    Read it as `01/02 03:04:05PM '06 -0700`: 1 is the month, 2 the day of month, 3 the hour on a 12-hour clock, 4 the minute, 5 the second, 6 the two-digit year (`2006` for four digits) and `-0700` the zone offset. The exception is `15`, the 24-hour hour, chosen because 15:04 is 3:04 PM.
  • How do you get a zero-padded day versus a space-padded one?
    `02` is zero padded (`05`), `_2` is space padded (` 5`), and a bare `2` is unpadded (`5`). The same three-way choice exists for the month as `01`, and for the day of year as `002` and `__2`. Picking the wrong one is only a cosmetic bug when formatting, but when parsing, a fixed-width element will refuse input of the wrong width.
  • What is the difference between the fractional-second elements `.000` and `.999`?
    `.000` is fixed width: it always prints that many digits, trailing zeros included. `.999` prints the same precision but trims trailing zeros, and omits the decimal point entirely when the fraction is zero. `time.RFC3339Nano` uses `.999999999`, which is why a whole-second timestamp comes out with no fractional part at all.

Instead of handing you a grammar of pattern letters, Go hands you one worked example — a single date already written out — and asks you to rewrite that same date in the shape you want.

saying these in an interview costs you the question

  • Says Go uses yyyy/MM/dd style pattern letters
  • Thinks 01 is the day and 02 the month
  • Expects Format to return an error for a bad layout
  • Writes 03 for the hour and omits PM
  • Believes the reference numbers are arbitrary rather than 1-7 in order
open as a page

What zone does time.Parse assign when the input carries no offset, and how does time.ParseInLocation differ?

level: middleimportance: must knowfreq 60%

basics

~20 s

time.Parse returns a time in UTC whenever the value carries no zone offset or abbreviation, so a local wall-clock string is silently read as UTC. time.ParseInLocation reads the same digits as wall-clock time in a *time.Location you supply.

open as a page

What does encoding/json emit for a time.Time field, and what timestamp input will it reject?

level: middleimportance: should knowfreq 52%

basics

~20 s

A time.Time marshals to a quoted RFC 3339 string with sub-second precision, because time.Time implements MarshalJSON. Decoding accepts only a quoted RFC 3339 string, so an epoch number or a space-separated datetime fails with an unmarshal error.

open as a page

A Go webhook parses partner dates with layout "01/02/2006", but the partner sends day-first. Why do the bugs appear only after the 12th?

level: seniorimportance: should knowfreq 44%

basics

~20 s

In a Go layout 01 is the month and 02 the day. Days of 12 or under are valid either way, so Parse succeeds and stores the wrong date. From the 13th the month is out of range and Parse errors.

open as a page

Should your webhook API mandate RFC 3339 timestamps or accept each partner's own date format at the edge?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Publish one required format and always emit it; accept deviations only as a short, explicitly enumerated per-partner list with fixtures and an end date. The cost of a permissive edge is not the parsing code, it is owning an unbounded accepted set forever.

open as a page