Writing layouts for time.Parse in Go

I recently started learning Go on Exercism. One of the topics in their syllabus was time/date parsing and manipulation. This is done using the time package in Go

Learning how to use the time package was hard. It is unlike anything I've seen before, mainly because of how time and date strings are parsed. Coming from JavaScript, I'd already had a pretty bad time working with times and dates. I wouldn't say Go was worse. Once I figured out how things worked, everything was smooth sailing.

Part of the exercise in the lesson was parsing different kinds of date/time strings and getting a Time interface from them. The time package had a Parse method that does this.

Here's the syntax for the Parse method:

func Parse(layout, value string) (Time, error)

Here, value is the date/time string that you want to parse. It returns a Time interface that could be used to do further manipulation on, and an error interface that's used for error handling. The layout parameter is where it gets tricky.

The thing with the Parse method is, we have to know how our date/time string is formatted ahead of time before we can parse it. Once we know the format, then we write a layout string that would represent our date/time string.

There is one other quirk to the layout value. We can't just use any date and time to represent our value. We have to use a specific date and time, predetermined by Go. This is the date and time we have to use, when constructing our layout:

January 2, 15:04:05 2006 MST

The reason Go devs picked this date and time is because there's an easy way to remember this:

// January 2, 15:04:05 2006 MST
// presented in this order, the converting each part to integer gives us
//       1 2   3  4  5    6  -7

Now that we know the exact date and time we have to use in our layout, we can mix and match the order, format, etc. Here are a few examples:

// Today: 10-05-2022
layout := "2-1-2006"

// Also today: 05-10-2022
layout := "1-2-2006"

// Date and time: May 10 2022 13:18:59 GMT+0530
layout := "Jan 2 2006 15:04:05 MST"

// Also date and time: May 10 2022 13:18:59 (IST)
layout := "Jan 2 2006 15:04:05 (MST)"

// ISO String: 2022-05-10T07:50:21.517Z
// time package provides a build-in way to parse ISO formatted date/time strings
// It works with both Z and timezone offsets
layout := time.RFC3339

// Day, date and time: Tue May 10 2022 13:18:59 GMT+0530
layout := "Mon Jan 2 2006 15:04:05 MST"

Now that we know how to write layouts, we're ready to parse date/time strings.

import "time"

func GetTime(date string) (Time, error) {
      layout := "Jan 2 2006 15:04:05 MST"
      return time.Parse(layout, date)
}

Now that we have a Time interface on our hands, we can use any of the methods available on that interface.

func GetIndividualValues(date string) {
      time_value, err := GetTime(date)
      hours, minutes, seconds := time_value.Clock()         
      year, month, day := time_value.Date()
      fmt.Printf("hours = %v\n", hours)
      fmt.Printf("minutes = %v\n", minutes)
      fmt.Printf("seconds = %v\n", seconds)
      fmt.Printf("year = %v\n", year)
      fmt.Printf("month = %v\n", month)
      fmt.Printf("day = %v\n", day)
}

// GetIndividualValues("May 10 2022 13:18:59 GMT+0530")
// hours = 13
// minutes = 18
// seconds = 59
// year = 2022
// month = May
// day = 10

Hope that helps in writing layouts. It was hard to understand at first but once I understood the concept, writing layouts became easier.