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(orJan) - Day:
02 - Hour:
15(24h) or03(12h) - Minute:
04 - Second:
05 - Timezone:
MSTor-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 usetime.Timewith JSON marshaling. Go handles serialization automatically. - Write a unit test for every new
time.Parsecall β 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)
}
}

