Fix Go 'parsing time as "2006-01-02": cannot parse' Time Format Error

beginnerπŸ”· Go2026-06-30| Go 1.13+, any OS (Linux, macOS, Windows), any project using time.Parse() or time.ParseInLocation()

Error Message

parsing time "2023/10/27" as "2006-01-02": cannot parse
#go#golang#time#time-parse#datetime#format#layout

The Error

You called time.Parse() and got something like this:

parsing time "2023/10/27" as "2006-01-02": cannot parse "/10/27" as "-"

Or maybe:

parsing time "27-10-2023 14:30:00" as "2006-01-02 15:04:05": cannot parse "27-10-2023" as "2006"

The timestamp is valid. Your code looks reasonable. But Go refuses to parse it.

Root Cause

Go's time formatting works differently from every other language. No YYYY-MM-DD. No %d/%m/%Y. Instead, Go uses a specific reference timestamp as the format template:

Mon Jan 2 15:04:05 MST 2006

Each component maps to a fixed reference value:

  • Year: 2006
  • Month: 01 (or Jan)
  • Day: 02
  • Hour: 15 (24h) or 03 (12h)
  • Minute: 04
  • Second: 05
  • Timezone: MST or -0700

So when your input is "2023/10/27" but your layout is "2006-01-02", Go tries to match slashes against dashes β€” and fails immediately.

// This FAILS β€” separator mismatch
t, err := time.Parse("2006-01-02", "2023/10/27")
// parsing time "2023/10/27" as "2006-01-02": cannot parse "/10/27" as "-"

Fix: Match the Layout to Your Input

Every separator in your layout must match exactly what's in your input string.

Input uses slashes: YYYY/MM/DD

t, err := time.Parse("2006/01/02", "2023/10/27")
if err != nil {
    log.Fatalf("parse error: %v", err)
}
fmt.Println(t) // 2023-10-27 00:00:00 +0000 UTC

Input uses dots: DD.MM.YYYY

t, err := time.Parse("02.01.2006", "27.10.2023")
// Note: day comes first, then month, then year β€” match your actual data order

Input is European format: DD-MM-YYYY

t, err := time.Parse("02-01-2006", "27-10-2023")

Input includes time: YYYY/MM/DD HH:MM:SS

t, err := time.Parse("2006/01/02 15:04:05", "2023/10/27 14:30:00")

Common Layouts Quick Reference

// Standard ISO 8601
time.Parse("2006-01-02", "2023-10-27")

// Slash-separated
time.Parse("2006/01/02", "2023/10/27")

// US format MM/DD/YYYY
time.Parse("01/02/2006", "10/27/2023")

// With time
time.Parse("2006-01-02 15:04:05", "2023-10-27 14:30:00")

// RFC3339 (ISO 8601 with timezone) β€” use the constant
time.Parse(time.RFC3339, "2023-10-27T14:30:00Z")

// RFC1123 β€” HTTP dates
time.Parse(time.RFC1123, "Fri, 27 Oct 2023 14:30:00 UTC")

// Unix timestamps β€” don't use Parse at all
t := time.Unix(1698416200, 0)

Debugging Unknown Input Formats

Consuming data from an external API and not sure what format they're sending? Print the raw string before you touch it:

rawDate := response["created_at"].(string)
fmt.Printf("raw date: %q\n", rawDate)
// raw date: "2023/10/27"
// Now you can see exactly what layout to write

Then map it character-by-character to Go's reference values:

Input:  2023/10/27
Layout: 2006/01/02
        ^^^^ ^^ ^^
        year mo day

Handle Multiple Possible Formats

Data from multiple sources rarely uses the same format. The simplest approach: try each layout until one succeeds.

func parseDate(s string) (time.Time, error) {
    layouts := []string{
        "2006-01-02",
        "2006/01/02",
        "01/02/2006",
        "02-01-2006",
        "2006-01-02T15:04:05Z07:00",
        time.RFC3339,
    }
    for _, layout := range layouts {
        if t, err := time.Parse(layout, s); err == nil {
            return t, nil
        }
    }
    return time.Time{}, fmt.Errorf("unrecognized date format: %q", s)
}

Timezone Pitfall

If your input includes a timezone offset but your layout doesn't account for it, you'll get a different error. Either include the timezone token in your layout, or use time.ParseInLocation:

// Input has offset: "2023/10/27 14:30:00 +0900"
t, err := time.Parse("2006/01/02 15:04:05 -0700", "2023/10/27 14:30:00 +0900")

// Or if you know the timezone and input has none:
loc, _ := time.LoadLocation("Asia/Tokyo")
t, err := time.ParseInLocation("2006/01/02", "2023/10/27", loc)

Verification

Once you've fixed the layout, run a quick sanity check. The fastest method is a round-trip: parse the string, then format it back and compare:

t, err := time.Parse("2006/01/02", "2023/10/27")
if err != nil {
    log.Fatalf("still failing: %v", err)
}

// Verify the fields
fmt.Println(t.Year())  // 2023
fmt.Println(t.Month()) // October
fmt.Println(t.Day())   // 27

// Round-trip: re-format back to the original string
fmt.Println(t.Format("2006/01/02")) // 2023/10/27

If the output matches your original input exactly, the layout is correct.

Prevention

  • Declare date layouts as named constants at the package level β€” scattered inline strings become a maintenance problem fast.
  • New layout? Do the round-trip test first. Parse a known date, format it back, and confirm it matches.
  • For APIs you control, standardize on RFC3339 (time.RFC3339) and use time.Time with JSON marshaling. Go handles serialization automatically.
  • Write a unit test for every new time.Parse call β€” use a real input/output pair, not just a "no error" check.
// Good practice: named constants
const (
    DateLayout     = "2006/01/02"
    DateTimeLayout = "2006/01/02 15:04:05"
)

// Unit test
func TestParseDate(t *testing.T) {
    got, err := time.Parse(DateLayout, "2023/10/27")
    if err != nil {
        t.Fatal(err)
    }
    if got.Day() != 27 || got.Month() != time.October || got.Year() != 2023 {
        t.Errorf("unexpected date: %v", got)
    }
}

Related Error Notes