DEV Community

Rajkumar
Rajkumar

Posted on

Practical guide to parsing your Date and Time in Go

#go

Gopher with a watch
Often we find ourselves with a Date Time that is not in some standard format. This is where we identify the pattern of the date time and then try to write a custom layout.

Let us see how we can achieve this in go using the standard time package. The time package provides us with several functionalities which includes functions like Parse and Format that serves our purpose.

Parse

Parse parses a formatted string and returns the time value it represents.

func Parse(layout, value string) (Time, error)
Enter fullscreen mode Exit fullscreen mode
package main

import (
    "fmt"
    "time"
)

func main() {
    const longForm = "Jan 2, 2006 at 3:04pm (MST)"
    t, _ := time.Parse(longForm, "Feb 3, 2013 at 7:54pm (PST)")
    fmt.Println(t)

    const shortForm = "2006-Jan-02"
    t, _ = time.Parse(shortForm, "2013-Feb-03")
    fmt.Println(t)
}
Enter fullscreen mode Exit fullscreen mode
Output:
2013-02-03 19:54:00 +0000 PST
2013-02-03 00:00:00 +0000 UTC
Enter fullscreen mode Exit fullscreen mode

Go Playground Link: Parse Example

Format

Format returns a textual representation of the time value formatted according to the layout defined by the argument.

func (t Time) Format(layout string) string
Enter fullscreen mode Exit fullscreen mode
func main() {
    t, err := time.Parse(time.UnixDate, "Wed Feb 25 11:06:39 PST 2015")
    if err != nil { // Always check errors even if they should not happen.
        panic(err)
    }

    fmt.Println("default format:", t)

    fmt.Println("Unix format:", t.Format(time.UnixDate))
}
Enter fullscreen mode Exit fullscreen mode
Output:
default format: 2015-02-25 11:06:39 +0000 PST
Unix format: Wed Feb 25 11:06:39 PST 2015
Enter fullscreen mode Exit fullscreen mode

Go Playground Link: Format Example

Predefined Layouts available in time package

Label Definition
Layout 01/02 03:04:05PM '06 -0700
ANSIC Mon Jan _2 15:04:05 2006
UnixDate Mon Jan _2 15:04:05 MST 2006
RubyDate Mon Jan 02 15:04:05 -0700 2006
RFC822 02 Jan 06 15:04 MST
RFC822Z 02 Jan 06 15:04 -0700
RFC850 Monday, 02-Jan-06 15:04:05 MST
RFC1123 Mon, 02 Jan 2006 15:04:05 MST
RFC1123Z Mon, 02 Jan 2006 15:04:05 -0700
RFC3339 2006-01-02T15:04:05Z07:00
RFC3339Nano 2006-01-02T15:04:05.999999999Z07:00
Kitchen 3:04PM
Stamp Jan _2 15:04:05
StampMilli Jan _2 15:04:05.000
StampMicro Jan _2 15:04:05.000000
StampNano Jan _2 15:04:05.000000000

Building Blocks for creating your own layout

Type Options
Year "2006", "06"
Month "Jan", "January", "1", "01"
Textual day of the week "Mon", "Monday"
Numeric day of the month "2", "_2", "02"
Numeric day of the year "__2", "002"
Hour "15", "3", "03" (PM or AM)
Minute "4", "04"
Second "5", "05"
AM/PM mark "PM"

For layouts specifying the two-digit year 06, a value NN >= 69 will be treated as 19NN and a value NN < 69 will be treated as 20NN.

Numeric time zone offsets format as follows:

Value Format
"-0700" ±hhmm
"-07:00" ±hh:mm
"-07" ±hh

Replacing the sign in the format with a Z triggers the ISO 8601 behavior of printing Z instead of an offset for the UTC zone. Thus:

Value Format
"Z0700" Z or ±hhmm
"Z07:00" Z or ±hh:mm
"Z07" Z or ±hh

Final Example

func main() {
    ipTimes := []string{
        "24 Aug 2021 7:20:50 PM",
        "Aug 24 2021 07:20:50PM",
        "24-08-2021 17:20:50",
        "08-24-2021 13:12:32",
        "24/8/21 9:00:12",
        "8/24/2021 10:10:10",
        "1993-11-23 7**12**44PM",
        "24 October 97 7:12:45PM+05:30", // 24 th January 1997
    }

    layouts := []string{
        "2 Jan 2006 3:04:05 PM",
        "Jan 2 2006 03:04:05PM",
        "2-01-2006 15:04:05",
        "01-02-2006 15:4:5",
        "2/1/06 3:4:5",
        "1/2/2006 3:4:5",
        "2006-01-02 3**4**5PM",
        "2 January 06 3:4:5PMZ07:00",
    }

    for i := 0; i < len(ipTimes); i++ {
        t, err := time.Parse(layouts[i], ipTimes[i]) // parsing to get time value
        if err != nil {                              // check for errors
            fmt.Println(err)
            return
        }

        fmt.Println("    Input DateTime String: ", ipTimes[i])
        fmt.Println("                   Layout: ", layouts[i])
        fmt.Println("Time value Default Format: ", t)
        fmt.Println("   Time in RFC3339 Format: ", t.Format(time.RFC3339))
    }
}

Enter fullscreen mode Exit fullscreen mode

Let us take one such example to iterate through it

Input: "24 October 97 7:12:45PM+05:30"
Layout: "2 January 06 3:4:5PMZ07:00"

From the building blocks listed above we can map things

Layout Key Actual Value Description
2 24 Numeric day of the month
January October month
06 97 Year (1997)
3 7 Hour
4 12 Minute
5 45 Second
Z07:00 +05:30 Offset

Since PM is specified 7:12:45 is in 12 hour format.

Using the Layout blocks specified we can easily see the pattern and can create our own layout.

Fun Tip

Ever wondered why time.Now() on golang playground prints the same time (2009-11-10 23:00:00 +0000 UTC m=+0.000000001) ?

Drum roll!!!!!!

It is the time and date of Go Lang's birthday.

Hope you learnt something new or had your concepts revised.

Let me know if I missed something.

Complete Working Date Time Example

References

Time Format
Time Parse
Constants

Discussion (0)