Working with Time Data in Go
Jacob Kim
Posted on February 7, 2023
On many occasions, you will have to write code that has to deal with time. You could write a clock program or measure the time difference between two points in your code. Either way, it's important to know how to manipulate time data in Go. It's pretty simple, and knowing some nifty functions can help you save time. Working with time data in Go requires you to import the time
package from the Go standard library. This package has a lot of methods and types that you can use, but I took the most used ones and described them here in this post. If you would like to see more info on the subject, check out the docs page for the time
package.
Tell the time
This is probably the most commonly used method in the package. How do we tell the current time? Like this:
t := time.Now()
fmt.Println(t)
2023-02-06 23:01:48.9983151 -0500 EST m=+0.000030901
That's the current time. It's pretty cumbersome, so I will break this down into components.
2023-02-06
: Year, month, day.23:01:48.9983151
: Hour, minutes, seconds-0500
: Time difference from GMT.EST
: The current time zone you are in.m=+0.000030901
: Monotonic clock reading.
We will get to monotonic clock later in this post. We can move on for now.
Is there a better way to format this?
You bet.
t := time.Now()
fmt.Println(t.Year())
fmt.Println(t.Month())
fmt.Println(t.Day())
fmt.Println(t.Date())
fmt.Println(t.Hour())
fmt.Println(t.Minute())
fmt.Println(t.Second())
2023
February
6
2023 February 6
23
26
6
Here's how you can extract each element of the time. Pretty straightforward, right?
How can we print this in a prettier format?
fmt.Printf("%d %d %d\n", t.Year(), t.Month(), t.Day())
2023 2 6
You can see how we can use the fmt.Printf
function to format the time to our liking.
But what if we want to show months by their names, like February instead of 2? What if we want to show the time on a 12-hour scale instead of 24? You can see how it gets complex very quickly.
There is a better way to format time
Fortunately, we have the time.Format
function to help us. Let's see how it works.
fmt.Println(t.Format("Mon Jan 2 15:04:05 2006 MST"))
fmt.Println(t.Format("Mon Jan 2 15:04:05"))
fmt.Println(t.Format("2006/01/02"))
fmt.Println(t.Format("3:04PM"))
fmt.Println(t.Format("15:04PM"))
Mon Feb 6 23:47:43 2023 EST
Mon Feb 6 23:47:43
2023/02/06
11:47PM
23:47PM
I think this is the part that tripped me up the most when I first started learning about this topic. And it's also the part where you can see how cheeky the language designers were.
We can see that time.Format
accepts a string that denotes the format we want our time to be in. This is the weird part, because Go is very, extremely particular about the format string's format.
Mon Jan 2 15:04:05 2006 MST
The format string has to be a variation of this string, or else the code prints out weird times. Interestingly enough, if you exclude the Mon
, each element of the format string represents an integer. Jan
is 1, 2
is 2, 15
is 3, etc. Nerdy people, these developers.
1 2 3 4 5 6 -7
From the code above, though, you can see how we can format our time the way we want it to. And we don't have to write extra functions to convert hours into 12 or 24-hour scales, or to map each integer to a month.
You could also use a predefined format as well, like so:
fmt.Println(time.UnixDate)
fmt.Println(time.RFC3339)
Mon Jan _2 15:04:05 MST 2006
2006-01-02T15:04:05Z07:00
Check the docs for time
package for more formats.
What about different time zones?
Time zones are automatically detected, as shown above. However, there may be cases where you need to show time in different time zones.
nt := time.Now()
lt := time.Now().Local()
ut := time.Now().UTC()
fmt.Println(nt)
fmt.Println(lt)
fmt.Println(ut)
2023-02-07 00:09:08.7052349 -0500 EST m=+0.000121601
2023-02-07 00:09:08.705235 -0500 EST
2023-02-07 05:09:08.705235 +0000 UTC
Local()
gets the local time zone, which is the same as what time.Now()
would automatically detect. Calling UTC()
will convert the time zone to UTC.
But what if we need something more robust?
l, _ := time.LoadLocation("UTC")
fmt.Printf("%s\n", nt.In(l))
l, _ = time.LoadLocation("Europe/London")
fmt.Printf("%s\n", nt.In(l))
l, _ = time.LoadLocation("America/Los_Angeles")
fmt.Printf("%s\n", nt.In(l))
l, _ = time.LoadLocation("Asia/Seoul")
fmt.Printf("%s\n", nt.In(l))
2023-02-07 05:12:10.4423244 +0000 UTC
2023-02-07 05:12:10.4423244 +0000 GMT
2023-02-06 21:12:10.4423244 -0800 PST
2023-02-07 14:12:10.4423244 +0900 KST
time.LoadLocation
will load a locale of your choice. You can use this result to convert your time by passing in to yourTime.In
.
You can also read time from strings
In many cases, you will have to read in a string. Often, these will be timestamps. Wouldn't it be great to use the time
library's functions on these timestamp strings?
By default, the time
library works on time.Time
types. However, we can parse these timestamp strings by using time.Parse
.
timestamp := "2023-02-06 23:49:01"
ts, err := time.Parse("2006-01-02 15:04:05", timestamp)
if err != nil {
fmt.Println(err)
}
fmt.Println(ts)
2023-02-06 23:49:01 +0000 UTC
You can also format ts
using the Format
method described above.
Wait, what is a monotonic clock?
Let's come back to this topic. It sounds a lot scarier than it actually is.
Your computer has a clock that measures time. Chances are that this time is rather volatile. Ever had the experience where your clock gets off by a couple of hours after traveling to another country? Ever had to set your clock again to match it with your watch? This clock is called the wall clock, and it is prone to change.
While the wall clock is good at telling the time, it's not good for measuring time. For instance, look at this piece of code.
t1 := time.Now()
fmt.Println(t1)
time.Sleep(time.Second())
t2 := time.Now()
fmt.Println(time.Now())
fmt.Println(t2.Sub(t1))
2023-02-06 23:23:32.2520709 -0500 EST m=+0.000031501
2023-02-06 23:23:33.2525152 -0500 EST m=+1.000475801
1.0004442s
The above code measures the time elapsed between t1
and t2
. This seems obvious because we waited one second before declaring t2
. But what if we somehow managed to switch our OS time settings in that span? If we add 4 hours to our system's clock, does that mean the time elapsed between t1
and t2
would be 4 hours and 1 second? That's ridiculous!
This is why Go uses a monotonic clock to measure time instead. You can see from the m
values in the printed times that the time difference is about one second. Monotonic clocks allow for accurate measurement of time.
Conclusion
We are so familiar with the concept of time that we tend to take our understanding of it for granted. However, time tends to be one of the more frustrating things to represent in a computer. Fortunately, Go developers have abstracted away most of the raw conversions from us, so that we can use the simple-to-understand functions of the time
package. This post covers a lot of the necessary functions, but if you need the nitty-gritty details, you can always refer to the official docs.
Thank you for reading! You can also view this post on Medium.
Posted on February 7, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.