This tutorial will teach you how to work with Time in Go (Golang) - including how to parse and format date-time strings, add and subtract time durations, get the current system time, and handle time zones effectively.

banner

The main library we’ll use when working with times and dates is the time standard library.

Getting the Current System Time

Let’s begin by creating a new Time instance from the current system time:

package main

import (
	"fmt"
	"time"
)

func main() {
	now := time.Now()
	fmt.Println(now.String()) // Output: 2009-11-10 23:00:00 +0000 UTC m=+0.000000001
}

After importing the time standard library, we call the time.Now() function, which returns the current system time.

Calling the String() method returns the time as a formatted string.

We can also return the time string in a custom format.

Formatting Time to a String

We can use the Format method of any Time instance to return a string in our custom format.

For example:

now := time.Now()
fmt.Println(now.Format("2 Jan 06 03:04PM")) // Output: 10 Nov 09 11:00PM 

The date and time in the argument of the Format method must be the same as the Layout constant, which is defined as:

01/02 03:04:05PM '06 -0700

Note: 01/02 refers to the American convention of MM/DD, which would be the 2nd of January.
'06 refers to the year 2006, and -0700 refers to the timezone.

The time library uses this as a reference to map different components of a string to the time that it’s referring to.

inputs are mapped to the reference string to generate the output

We can also use some of the formats defined in the constants to conveniently format our string into recognized standard formats.

For example, we can use the time.UnixDate layout format to return a Unix-formatted date string:

now := time.Now()
fmt.Println(now.Format(time.UnixDate)) // Output: Tue Nov 10 23:00:00 UTC 2009

Try it yourself by running this code in the Go Playground.

Parsing Time Strings

The time.Parse function takes a string as input and returns a Time instance. The time library uses the same layout mechanism from the previous section to convert a string into a Time instance.

Here’s how to parse a custom-formatted string:

myTime, err := time.Parse("2 Jan 06 03:04PM", "10 Nov 09 11:00PM") 
if err != nil {
	panic(err) 
}
fmt.Println(myTime) 

Try it yourself by running this code in the Go Playground.

This is essentially the inverse of the Format method.

parse uses the layout string to convert an input string to a time instance

Duration - Adding and Subtracting Time

Go uses the Duration type to represent the difference between two instances of time.

myTime, err := time.Parse("2 Jan 06 03:04PM", "10 Nov 09 11:00PM")
if err != nil {
	panic(err)
}
now := time.Now()
difference := myTime.Sub(now) 
fmt.Println(difference) // Output: 8760h0m0s

Try it yourself by running this code in the Go Playground.

The difference variable is of type Duration and represents an elapsed duration of time.

Note: If the first time instance is before the second, the duration will be negative.

We can convert this duration into different units like hours or milliseconds:

fmt.Println(difference.Hours())      // Output: 8760
fmt.Println(difference.Milliseconds()) // Output: 31536000000

Note: The largest representable duration is approximately 290 years. To represent durations larger than that, you’ll need to use a custom type.

We can also add a duration to an existing time using the Add method:

now := time.Now()
later := now.Add(3 * time.Hour)
fmt.Println("now: ", now, "\nlater: ", later)

Output (try it yourself):

now:  2009-11-10 23:00:00 +0000 UTC m=+0.000000001 
later:  2009-11-11 02:00:00 +0000 UTC m=+10800.000000001

For readability, use duration constants like time.Hour, time.Minute, time.Second, and time.Millisecond:

3 * time.Hour     // 3 hours
16 * time.Minute   // 16 minutes
120 * time.Second  // 120 seconds

Time Comparison

To compare time instances, use the Before, After, and Equal methods:

myTime, err := time.Parse("2 Jan 06 03:04PM", "11 Nov 09 11:00PM")
if err != nil {
	panic(err)
}
now := time.Now()
fmt.Println(now.Before(myTime)) // Output: true
fmt.Println(now.After(myTime))  // Output: false
fmt.Println(now.Equal(myTime))  // Output: false

Try it yourself by running this code in the Go Playground.

Equal checks for equality in both value and location, making it more robust than simply comparing timestamps.

Getting Unix/Epoch Time

Unix or Epoch time represents the number of seconds elapsed since January 1, 1970 UTC.

Get the epoch time using the Unix and UnixMilli methods:

now := time.Now()
fmt.Println(now.Unix())       // Output: 1257894000
fmt.Println(now.UnixMilli())  // Output: 1257894000000

Working with Time Zones

You’ll be surprised how many bugs can be traced back to incorrect time zone handling. Proper time zone management is crucial for accurate time calculations.

Use time.LoadLocation to get a specific time zone and time.In to assign it to a Time instance.

loc, err := time.LoadLocation("America/New_York")
if err != nil {
	panic(err)
}
now := time.Now().In(loc)
fmt.Println("Current time in New York:", now) // Output: Current time in New York: 2009-11-10 18:00:00 -0500 EST

Try it yourself by running this code in the Go Playground.

Convert a time to UTC using the UTC() method:

utcTime := now.UTC() 
fmt.Println(utcTime) 

Avoid using time.Local unless you’re certain you need the system’s local time zone.

Using time.Ticker and time.Timer

time.Ticker allows you to execute code at regular intervals:

ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop() // Ensure clean-up

for {
	<-ticker.C 
	// Code to execute every 5 seconds
}

time.Timer is used for one-off events after a specific duration:

timer := time.NewTimer(10 * time.Second)
defer timer.Stop() 

<-timer.C 
// Code to execute after 10 seconds

The Time/tzdata Package

The time/tzdata package, introduced in Go 1.15, provides access to the IANA Time Zone Database. Ensure this package is available in your application for accurate time zone information.

Best Practices and Further Reading

This post covered essential concepts for working with time and dates in Go. Some tips to avoid common pitfalls:

  1. Explicitly Manage Time Zones: Always be aware of and explicitly handle time zones to avoid subtle bugs.
  2. Use Duration Constants: Utilize duration constants (time.Hour, time.Minute, etc.) for better code clarity.

For a deeper dive, consult the official time package documentation.