RFC 3339 in Go: 5 Date Parsing Errors and How to Fix Them

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Parsing RFC 3339 / ISO-8601 in Go: Why Even Experienced Developers Trip Up

\n\n

Have you ever written something like time.Parse("2006-01-02T15:04:05Z", input) in Go, got an error, and felt like the language was deliberately mocking you? Sounds familiar. Parsing dates and times is one of those topics where everything seems simple, but in practice, many pitfalls await.

\n\n

On Stack Overflow, the question "Parsing RFC-3339 / ISO-8601 date-time string in Go" has gathered thousands of views. People get confused with formats, forget about nanoseconds, struggle with time zones, and get frustrated by the "magic" number 2006-01-02T15:04:05Z. In this article, we'll break down the 5 most common errors and show how to avoid them once and for all. Let's go!

\n\n

A Bit of Theory: What Are RFC 3339 and ISO 8601?

\n\n

Before diving into the code, let's quickly clarify the terms. ISO 8601 is an international standard for representing dates and times. RFC 3339 is a "stripped-down" version of it, popular on the web (e.g., in JSON, APIs, logs).

\n\n

Typical RFC 3339 strings look like this:

\n\n
2025-01-15T10:30:00Z\n2025-01-15T10:30:00+03:00\n2025-01-15T10:30:00.123456789Z
\n\n

In Go, the time.Parse(layout, value) function is used for parsing. And this is where the fun begins.

\n\n

Error #1: Incorrect Layout — The Main Cause of Headaches

\n\n

Go uses a unique approach: the format is specified not with codes like YYYY-MM-DD, but with a specific date: Mon Jan 2 15:04:05 MST 2006. It's easy to remember: 01/02 03:04:05PM '06 -0700 (month, day, hour, minute, second, year, time zone).

\n\n

Beginners often try to write something like:

\n\n
// ❌ Error!\nt, err := time.Parse("YYYY-MM-DDTHH:MM:SSZ", "2025-01-15T10:30:00Z")
\n\n

Go simply won't understand this format. The correct layout for RFC 3339 is time.RFC3339:

\n\n
// ✅ Correct\nt, err := time.Parse(time.RFC3339, "2025-01-15T10:30:00Z")\nif err != nil {\n    log.Fatal(err)\n}\nfmt.Println(t) // 2025-01-15 10:30:00 +0000 UTC
\n\n

The constant time.RFC3339 equals the string "2006-01-02T15:04:05Z07:00". Note the Z07:00 — this is the time zone notation.

\n\n

Error #2: Ignoring Nanoseconds (time.RFC3339 vs time.RFC3339Nano)

\n\n

The standard time.RFC3339 does not support nanoseconds. If your string contains a fractional second:

\n\n
2025-01-15T10:30:00.123456789Z
\n\n

…then parsing with time.RFC3339 will return an error:

\n\n
// ❌ Error: parsing time "2025-01-15T10:30:00.123456789Z" as "2006-01-02T15:04:05Z07:00": cannot parse ".123456789Z" as "Z07:00"\nt, err := time.Parse(time.RFC3339, "2025-01-15T10:30:00.123456789Z")
\n\n

The solution is to use time.RFC3339Nano:

\n\n
// ✅ Correct\nt, err := time.Parse(time.RFC3339Nano, "2025-01-15T10:30:00.123456789Z")\nif err != nil {\n    log.Fatal(err)\n}\nfmt.Println(t) // 2025-01-15 10:30:00.123456789 +0000 UTC
\n\n

Tip: If you're unsure whether nanoseconds will be present, use time.RFC3339Nano — it's backward compatible and will work for both cases.

\n\n

Error #3: Time Zone — Is Z or +00:00 the Same Thing?

\n\n

The strings 2025-01-15T10:30:00Z and 2025-01-15T10:30:00+00:00 mean the same thing (UTC), but Go may handle them differently if the layout is specified incorrectly.

\n\n
// ✅ Both variants work with time.RFC3339\nt1, _ := time.Parse(time.RFC3339, "2025-01-15T10:30:00Z")\nt2, _ := time.Parse(time.RFC3339, "2025-01-15T10:30:00+00:00")\nfmt.Println(t1.Equal(t2)) // true
\n\n

But if you write your own layout, be careful:

\n\n
    \

Blogs

Book Recommendations