Out of the box, Go (Golang) supports time formatting. In this tutorial, we will walk through some examples of working with time formatting in Golang. When formatting a date time, you have to specify pattern-based layouts and use the Format()
function in the time package:
func (t Time) Format(layout string) string
: Format returns a textual representation of the time value formatted according to the layout defined by the argument. See the documentation for the constant called Layout to see how to represent the layout format.
The Unique Nature of Golang's Time Formatting
Golang, in its commitment to clarity and simplicity, handles time formatting and parsing in a way distinct from most other programming languages. Instead of using format specifiers like %Y
, %m
, %d
, etc., as seen in languages like Python or C, Go uses a reference time to dictate the layout.
The Reference Time: "Mon Jan 2 15:04:05 MST 2006"
This string is a representation of a specific moment: Monday, 02 January 2006, 15:04:05 PM, in the Mountain Standard Time zone
. This moment is not arbitrary, though; each component of the reference time serves as a placeholder:
Mon
- abbreviated weekdayJan
- abbreviated month2
- day of the month15
- hour (24-hour clock)04
- minute05
- secondMST
- timezone abbreviation2006
- year
When you want to format (or parse) a time in Go, you provide a layout string that uses these components in the desired order and format.
For example, if you want a layout that represents a date in the format "YYYY-MM-DD", you'd use the layout 2006-01-02
. If you're looking to format a time like "HH:mm:ss", then the layout string would be 15:04:05
.
Supported Golang Time Layouts
You can refer golang time format page to get the list of supported cost values to manipulate the time output. Here is a table depicting the same information:
Unit | Golang Layout | Examples | Note |
---|---|---|---|
Year | 06 | 21, 81, 01 | |
Year | 2006 | 2021, 1981, 0001 | |
Month | January | January, February, December | |
Month | Jan | Jan, Feb, Dec | |
Month | 1 | 1, 2, 12 | |
Month | 01 | 01, 02, 12 | |
Day | Monday | Monday, Wednesday, Sunday | |
Day | Mon | Mon, Wed, Sun | |
Day | 2 | 1, 2, 11, 31 | |
Day | 02 | 01, 02, 11, 31 | zero padded day of the month |
Day | _2 | ⎵1, ⎵2, 11, 31 | space padded day of the month |
Day | 002 | 001, 002, 011, 031, 145, 365, 366 | zero padded day of the year |
Day | __2 | ⎵⎵1, ⎵⎵2, ⎵11, ⎵31, 365, 366 | space padded day of the year |
Part of day | PM | AM, PM | |
Part of day | pm | am, pm | |
Hour 24h | 15 | 00, 01, 12, 23 | |
Hour 12h | 3 | 1, 2, 12 | |
Hour 12h | 03 | 01, 02, 12 | |
Minute | 4 | 0, 4 ,10, 35 | |
Minute | 04 | 00, 04 ,10, 35 | |
Second | 5 | 0, 5, 25 | |
Second | 05 | 00, 05, 25 | |
10-1Â to 10-9Â s | .0 .000000000 | .1, .199000000 | Trailing zeros included |
10-1Â to 10-9Â s | .9 .999999999 | .1, .199 | Trailing zeros omitted |
Time zone | MST | UTC, MST, CET | |
Time zone | Z07 | Z, +08, -05 | Z is for UTC |
Time zone | Z0700 | Z, +0800, -0500 | Z is for UTC |
Time zone | Z070000 | Z, +080000, -050000 | Z is for UTC |
Time zone | Z07:00 | Z, +08:00, -05:00 | Z is for UTC |
Time zone | Z07:00:00 | Z, +08:00:00, -05:00:00 | Z is for UTC |
Time zone | -07 | +00, +08, -05 | |
Time zone | -0700 | +0000, +0800, -0500 | |
Time zone | -070000 | +000000, +080000, -050000 | |
Time zone | -07:00 | +00:00, +08:00, -05:00 | |
Time zone | -07:00:00 | +00:00:00, +08:00:00, -05:00:00 |
Supported Golang Time Formats
You can refer golang time format page to get the list of supported cost values to manipulate the time output. Here is a table depicting the same information:
Format | Example |
---|---|
ANSIC | Tue Oct 4 18:54:28 2022 |
UnixDate | Tue Oct 4 18:54:28 +07 2022 |
RubyDate | Tue Oct 04 18:54:28 +0700 2022 |
RFC822 | 04 Oct 22 18:54 +07 |
RFC822Z | 04 Oct 22 18:56 +0700 |
RFC850 | Tuesday, 04-Oct-22 18:54:28 +07 |
RFC1123 | Tue, 04 Oct 2022 18:54:28 +07 |
RFC1123Z | Tue, 04 Oct 2022 18:54:28 +0700 |
RFC3339 | 2022-10-04T18:54:28+07:00 |
RFC3339Nano | 2022-10-04T18:54:28.0977814+07:00 |
Format time output with a pre-defined format
Based on the above formats, we can easily format a time value using any of those constants. Let's look at an example.
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now.Format(time.ANSIC)) // Tue Oct 4 18:56:48 2022
fmt.Println(now.Format(time.UnixDate)) // Tue Oct 4 18:56:48 +07 2022
fmt.Println(now.Format(time.RubyDate)) // Tue Oct 04 18:56:48 +0700 2022
fmt.Println(now.Format(time.RFC822)) // 04 Oct 22 18:56 +07
fmt.Println(now.Format(time.RFC822Z)) // 04 Oct 22 18:56 +0700
fmt.Println(now.Format(time.RFC850)) // Tuesday, 04-Oct-22 18:56:48 +07
fmt.Println(now.Format(time.RFC1123)) // Tue, 04 Oct 2022 18:56:48 +07
fmt.Println(now.Format(time.RFC1123Z)) // Tue, 04 Oct 2022 18:56:48 +0700
fmt.Println(now.Format(time.RFC3339)) // 2022-10-04T18:56:48+07:00
fmt.Println(now.Format(time.RFC3339Nano)) // 2022-10-04T18:56:48.74371+07:00
}
Format a time with a customized layout (YYYYMMDDHHMMSS)
In the below example, we will use Format()
function to change the date time's format. We have to define the layout first, then pass it as parameter to Format()
function:
package main
import (
"fmt"
"time"
)
func displayDate(format string) {
fmt.Println(time.Now().Format(format))
}
func main() {
// The output time will vary for you
// but you can check the time format in which we get the output
displayDate("20060102150405") // YYYYMMDDHHMMSS Format
displayDate("2006/01/02 15/04/05") // YYYY/MM/DD HH/MM/SS Format
displayDate("02/01/2006 15/04/05") // DD/MM/YYYY HH/MM/SS Format
displayDate("02/01/06 15/04/05") // DD/MM/YY HH/MM/SS Format
displayDate("2006-01-02 15:04:05") // YYYY-MM-DD HH:MM:SS Format
displayDate("15:04:05, 2006-Jan-02 Mon") // HH:MM:SS, YYYY-Mon-DD DayShort Format
displayDate("15:04:05, 2006-Jan-02 Monday") // HH:MM:SS, YYYY-Mon-DD DayLong Format
displayDate("15:04:05, 2006-January-02 MST Mon") // HH:MM:SS, YYYY-Mon-DD TZ DayShort Format
displayDate("3:4:05, 2006-1-02 MST Mon") // H:M:SS, YYYY-M-DD TZ DayShort Format
displayDate("3:4:05 pm, 2006-1-02 MST Mon") // H:M:SS Part_of_day(small), YYYY-M-DD TZ DayShort Format
displayDate("3:4:05 PM, 2006-1-02 MST Mon") // H:M:SS Part_of_day(small), YYYY-M-DD TZ DayShort Format
displayDate("03:04:05.000") // HH:MM:SS.MilliSeconds
displayDate("03:04:05.000000") // HH:MM:SS.MicroSeconds
displayDate("03:04:05.000000000") // HH:MM:SS.NanoSeconds
}
Output:
# go run main.go
20221007002045
2022/10/07 00/20/45
07/10/2022 00/20/45
07/10/22 00/20/45
2022-10-07 00:20:45
00:20:45, 2022-Oct-07 Fri
00:20:45, 2022-Oct-07 Friday
00:20:45, 2022-October-07 IST Fri
12:20:45, 2022-10-07 IST Fri
12:20:45 am, 2022-10-07 IST Fri
12:20:45 AM, 2022-10-07 IST Fri
12:20:45.618
12:20:45.618877
12:20:45.618882881
Time is measured in nanosecond precision, but the computer may not be able to measure time passing at this resolution, so steps in time may happen in multiple nanoseconds. The time
 package has these durations built-in:
- Nanosecond Duration = 1
- Microsecond = 1000 * Nanosecond
- Millisecond = 1000 * Microsecond
- Second = 1000 * Millisecond
- Minute = 60 * Second
- Hour = 60 * Minute
Golang time parse format from strings
The time package provides support for creating Time values from strings
Parse(layout, str)
: This function parses a string using the specified layout to create a Time value. An error is returned to indicate problems parsing the string.ParseInLocation(layout, str, location)
: This function parses a string, using the specified layout and using the Location if no time zone is included in the string. An error is returned to indicate problems parsing the string.
Using Parse() function
The Parse function assumes that dates and times expressed without a time zone are defined in Coordinated Universal Time (UTC). The ParseInLocation
 method can be used to specify a location that is used when no time zone is specified
func Parse(layout, value string) (Time, error)
: Parse parses a formatted string and returns the time value it represents. The second argument must be parseable using the format string (layout) provided as the first argument.
We can use a pre-defined layout, as shown in Example 1, or a customized one, as shown in Example 2. Here's an example of a string being parsed to a specific data format using Parse()
function:
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Time parsing")
dateString := "2014-11-09T12:40:45.15Z"
dateString2 := "2022-10-12T10:15:16.371+07:00"
time1, err := time.Parse(time.RFC3339, dateString)
if err != nil {
fmt.Println("Error while parsing date :", err)
}
fmt.Println(time1)
time2, err := time.Parse(time.RFC3339, dateString2)
if err != nil {
fmt.Println("Error while parsing date :", err)
}
fmt.Println(time2)
dateString3 := "2007-10-09 22:50:02.023"
time3, err := time.Parse("2006-01-02 15:04:05.000", dateString3)
if err != nil {
fmt.Println("Error while parsing date :", err)
}
fmt.Println(time3)
}
Output:
Time parsing
2014-11-09 12:40:45.15 +0000 UTC
2022-10-12 10:15:16.371 +0700 +07
2007-10-09 22:50:02.023 +0000 UTC
Using ParseInLocation() function
func ParseInLocation(layout, value string, loc *Location) (Time, error)
: ParseInLocation is like Parse but differs in two important ways. First, in the absence of time zone information, Parse interprets a time as UTC; ParseInLocation
interprets the time as in the given location. Second, when given a zone offset or abbreviation, Parse tries to match it against the Local location; ParseInLocation
uses the given location.
Here's an example convert a string to a specific data format, consider the location and time zone:
package main
import (
"fmt"
"time"
)
func PrintTime(label string, t *time.Time) {
fmt.Println(label, t.Format(time.RFC822Z))
}
func main() {
// Choose your layout MM YYY DD HH:SS
layout := "02 Jan 06 15:04"
// Format this date with Location Timezone
date := "09 Jun 22 19:30"
// Load the locations to Parse
india, inerr := time.LoadLocation("Asia/Kolkata")
newyork, nycerr := time.LoadLocation("America/New_York")
if inerr == nil && nycerr == nil {
nolocation, _ := time.Parse(layout, date)
indiaTime, _ := time.ParseInLocation(layout, date, india)
newyorkTime, _ := time.ParseInLocation(layout, date, newyork)
PrintTime("No location:", &nolocation)
PrintTime("India:", &indiaTime)
PrintTime("New York:", &newyorkTime)
} else {
fmt.Println(inerr.Error(), nycerr.Error())
}
}
Output:
$ go run main.go
No location: 09 Jun 22 19:30 +0000
India: 09 Jun 22 19:30 +0530
New York: 09 Jun 22 19:30 -0400
Using time.ParseDuration() function
The time.ParseDuration
 function parses strings to create Duration values. This function returns a Duration and an error, indicating if there were problems parsing the specified string.
Following table describes the Duration
String Unit Indicators
Unit | Description |
---|---|
h | This unit denotes hours. |
m | This unit denotes minutes. |
s | This unit denotes seconds. |
ms | This unit denotes milliseconds. |
us or μs | These units denotes microseconds. |
ns | This unit denotes nanoseconds. |
No spaces are allowed between values, which can be specified as integer or floating-point amounts.
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
d, err := time.ParseDuration("1h30m")
if err == nil {
Printfln("Hours: %v", d.Hours())
Printfln("Mins: %v", d.Minutes())
Printfln("Seconds: %v", d.Seconds())
Printfln("Millseconds: %v", d.Milliseconds())
} else {
fmt.Println(err.Error())
}
}
The string specifies 1 hour and 30 minutes.
$ go run main.go Hours: 1.5 Mins: 90 Seconds: 5400 Millseconds: 5400000Â
Localized Time Formatting in Go
When building applications that cater to a global audience or operate across multiple geographies, dealing with time in various locales and time zones becomes essential. This section will discuss handling different locales and time zones in Go and some challenges and solutions associated with it.
Golang's standard library doesn't provide direct locale-based date and time formatting out of the box. However, locales influence several aspects of time representation:
- Weekday and Month Names: In English, we have "Monday" or "January." In French, they'd be "Lundi" or "Janvier."
- Order of Elements: Some locales might represent dates as YYYY-MM-DD, while others may prefer DD-MM-YYYY.
- First Day of the Week: In many western countries, Sunday is the first day of the week. In others, it's Monday.
Use third-party libraries like golang.org/x/text/message
for localization. This package provides functionality for locale-specific messaging.
Handling Different Time Zones
With Go's time
package, handling different time zones becomes relatively straightforward:
Loading a Location:
loc, err := time.LoadLocation("America/New_York")
if err != nil {
panic(err)
}
Converting a Time to a Specific Location:
t := time.Now()
tInNewYork := t.In(loc)
UTC: UTC is the universal time standard. To get a time in UTC, use:
tUTC := t.UTC()
Operations with Time in Go
When working with time in Go, there are often needs to perform various arithmetic operations, such as adding or subtracting intervals, or even comparing two time points. The Go time
package offers robust tools for these tasks.
Differences between time.Duration
and Formatted Time
time.Duration
:- Represents a span of time.
- Is an int64 representation of nanoseconds, so it's a precise value.
- Has constants like
time.Second
,time.Minute
, etc., for human-readable duration values. - Can be positive (to represent durations after a time point) or negative (for durations before a time point).
- Formatted Time:
- Represents a specific point in time.
- Is an instance of the
time.Time
type. - Can be output in various string formats using the
Format
method. - Is timezone-aware, whereas
time.Duration
is just a span of time and doesn't concern itself with time zones.
Arithmetic Operations
Adding Time:
To add a duration to a time.Time
value, use the Add
method.
t := time.Now()
oneHourLater := t.Add(1 * time.Hour)
Subtracting Time:
To subtract a duration, you can use the Add
method with a negative duration, or the Sub
method to get the duration between two times.
t := time.Now()
oneHourEarlier := t.Add(-1 * time.Hour) // Using Add with negative duration
timePassed := t.Sub(oneHourEarlier) // Using Sub to get the duration
Comparing Times:
Use the methods Before
, After
, and Equal
on a time.Time
value to compare it with another.
t1 := time.Now()
t2 := t1.Add(1 * time.Hour)
if t1.Before(t2) {
fmt.Println("t1 is before t2")
}
if t1.Equal(t2) {
fmt.Println("t1 is the same time as t2")
}
Duration Arithmetic:
You can also perform arithmetic operations on time.Duration
values directly.
duration1 := 2 * time.Hour
duration2 := 30 * time.Minute
total := duration1 + duration2 // Results in 2.5 hours
Round and Truncate:
- The
Round
method rounds atime.Duration
to the nearest multiple of another duration. - The
Truncate
method truncates atime.Duration
to the nearest multiple of another duration but doesn't round up.
d := 1*time.Hour + 41*time.Minute
fmt.Println(d.Round(1 * time.Hour)) // 2h0m0s
fmt.Println(d.Truncate(1 * time.Hour)) // 1h0m0s
Summary
In this article, I demonstrated how to perform golang time format using a predefine layout such as YYYYDDMM HH:MM:SS or print the time in nanoseconds, milliseconds, microseconds etc. Convert a string to a specific date format with time.Parse()
and time.Format()
. We can use either predefined or custom layouts.
References
https://www.timeanddate.com/time/map/
https://pkg.go.dev/time#ParseInLocation