Skip to Content
Course content

123: Formatting and Parsing Time in Go

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I'll be honest: the first time I tried to format a date in Go, I spent twenty minutes staring at the documentation in complete confusion. Coming from Python or C#, I kept looking for the %Y-%m-%d style format strings. I tried to guess the symbols, I tried to find a hidden strftime function, and I felt like I was losing my mind. It turns out, Go does things in a way that feels completely backwards until you realize the logic behind it.

The muscle memory trap

When you're new to Go, your instinct is to treat time formatting as a set of symbolic codes. You might try something like this, thinking you're being clever with string manipulation or searching for a format library:

// This is the "naive" approach: trying to manually slice strings 
// or searching for a %Y-%m-%d equivalent that doesn't exist.
timestamp := "2023-10-27 14:30:05"
// I've seen people try to use fmt.Sscanf or manual splitting here
// just to avoid learning the "Go way." It's brittle and slow.

The problem with trying to force C-style formatting into Go is that you'll either end up writing a lot of boilerplate code to slice strings, or you'll spend hours fighting a library that isn't designed for that. The "wrong" way isn't just about the code; it's about fighting the language's design. Go doesn't use symbols because symbols like %M (minute) and %m (month) are too easy to swap by accident, leading to those classic bugs where your logs show the month as the minute.

The magic of the reference date

In Go, you don't use symbols. You use a specific point in time as your layout. If you want to tell Go how to format a date, you show it an example of what that date should look like, using this exact magic date: January 2nd, 3:04:05 PM, 2006, MST (Mountain Standard Time).

I remember the "aha!" moment when I realized why this specific date was chosen. Look at the numbers: 1 (month), 2 (day), 3 (hour), 4 (minute), 5 (second), 6 (year). It's a sequence. Once you memorize that sequence, you stop guessing. To format a date as 2023-10-27, you don't use %Y-%m-%d; you use 2006-01-02.

now := time.Now()
// Instead of symbols, we use the "Layout" string
fmt.Println(now.Format("2006-01-02 15:04:05")) 
// Output: 2023-10-27 14:30:05 (depending on current time)

This is significantly more intuitive once it clicks because the layout string is the example. If you want the month name written out, you just write "January" in your layout. If you want a leading zero on the day, you write "02".

Parsing back from strings

Parsing is just the reverse of formatting. You provide the same reference layout to time.Parse, and Go figures out how to map the input string back into a time.Time object. This is where most bugs creep in, usually because of a mismatch between the layout and the actual data.

input := "2023-10-27"
layout := "2006-01-02"
t, err := time.Parse(layout, input)
if err != nil {
    // This is where you'll catch the "parsing time ... as ...: cannot parse" errors
    log.Fatal(err)
}

One thing to watch out for: time.Parse assumes UTC unless you specify a timezone in the layout or use time.ParseInLocation. I've seen countless production bugs where a developer parsed a local timestamp without specifying the location, and the system suddenly shifted all their appointments by 5 or 8 hours. If you're dealing with user-provided local times, always use ParseInLocation.

When to stop doing it manually

Before you spend time crafting a custom layout, check if what you need is already a standard. Go provides constants like time.RFC3339 (the gold standard for APIs) and time.RFC1123. If you're building a professional API, please, for the love of everything, just use time.RFC3339. It handles the offset and the ISO 8601 standard automatically, saving you from the headache of managing timezone offsets manually.




📋 Practical Task

Exercise: Legacy Log Timestamp Converter

You've been handed a legacy log file where the timestamps are in a very strange, non-standard format: "Jan 02, 2006 03:04PM" (e.g., "Oct 27, 2023 02:30PM"). Your task is to write a function that converts these legacy strings into standard RFC3339 formatted strings.

Requirements:

  • Create a function ConvertLegacyTime(input string) (string, error).
  • Use time.Parse with the correct reference layout to handle the legacy format.
  • Return the time formatted using the time.RFC3339 constant.
  • Handle the error case where the input string does not match the legacy format.

Test Case:
Input: "Oct 27, 2023 02:30PM"
Expected Output: A string resembling "2023-10-27T14:30:00Z" (the exact offset may vary based on your local environment, but the format must be RFC3339).

Rating
0 0

There are no comments for now.

to be the first to leave a comment.