Let's recap what we already know about printing time in Go. There are two ways to do it. The first one is to print everything: 1941-09-09 00:00:00 +0000 UTC. This is informative but doesn't look pretty. The second way is to print only the date — 1941 September 9.
However, what if we only wanted to print the year and month, or just hours and minutes? What if we are required to use a different format? For example, day/month/year? Or use a different symbol as a separator? And how to create a time variable from a string?
In this topic, you will get answers to these questions and learn how to format time variables in Go.
Year, month, and day
The Date() function returns a formatted date. You need to know that the returned date value is actually not a string, another special type, or struct. Calling the Date() function on time.Now() returns three variables: year and day of the int type and month of the time.Month type.
You can use it to format a date:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
year, month, day := now.Date()
fmt.Print("Original: ")
fmt.Println(now.Date())
fmt.Printf("Formatted: It's %v %v of %v\n", month, day, year)
}
// Output:
// Original: 2022 April 14
// Formatted: It's April 14 of 2022
Print functions. This feature is provided by the multivariable output of Date(). If we try to write print statements like fmt.Printf("%v", now.Date()) or fmt.Printf("%v %v %v", now.Date()), we will get the following error: multiple-value t.Date() in single-value context.Time units
You already know how to get the year, month, and day; however, hours, minutes, etc. are not available yet. Remember that using the Date() function returns only three variables — year, month, and day. Now, what if you wanted to get year separate from other variables? Or just month? Or get hours, minutes, and seconds?
The time.Time type has functions that can help you get separated values. Moreover, you can get a day of the week (Monday, Tuesday, Wednesday, etc.) or day of the year (1st, 2nd, or 256th). Let's first take a look at how to get basic time units:
Year(),Month(),Day(),Hour(),Minute(),Second(),Nanosecond(),Location().
It's easy to remember: just get the name of the time unit, make the capital first letter and write brackets at the end. By using these functions, you can get any information from time.Time.
Now, let's take a look at other useful functions:
YearDay()returns a number of a day in the year;Weekday()returns the day of the week.
All these methods don't need any arguments and are called on time.Time:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println("Year:", now.Year())
fmt.Println("Month:", now.Month())
fmt.Printf("Time: %v:%v\n", now.Hour(), now.Minute())
fmt.Println("Weekday:", now.Weekday())
}
// Output:
// Year: 2022
// Month: April
// Time: 11:35
// Weekday: Monday
Be careful with Weekday(). Even though the output is Monday, it's only the result of formatting! Do you remember the value that the Duration type prints? Even though its formatted output has the 00h00m00s format, it keeps time in nanoseconds as the int64 type. The Weekday() function works similarly: it keeps an int type value for each day of the week (0 is Sunday; 6 is Saturday).
Weekday() with a built-in constant. The time package contains a set of constants for all weekdays names; for example, Monday is time.Monday. So if you want to know what Weekday() is Monday, you need to use an expression like now.Weekday() == time.Monday. Templates and format
You are already familiar with one of the most popular time layouts — 1903-10-04 00:00:00 +0000 UTC. It corresponds to the template named RFC3339. This template contains a string that determines how we must place the units of time (year, month, day, hour, etc.). The time package contains constants that keep strings of templates.
Let's look at the strings that the two most common layouts contain and try to represent time using them:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(time.RFC3339)
fmt.Println(time.Kitchen)
fmt.Println(now)
fmt.Println(now.Format(time.RFC3339))
fmt.Println(now.Format(time.Kitchen))
}
// Output:
// 2006-01-02T15:04:05Z07:00
// 3:04PM
// 2022-04-05 17:10:03.933495 +0500 +05 m=+0.000062585
// 2022-04-05T17:10:03+05:00
// 5:10PM
The third, fourth, and fifth output strings contain the same variable but in different formats.
Let's pay attention to the first two strings. These strings already contain some dates. It's not random: each number is a component of a layout. Using these numbers, you can create your time format. For example, the component 2006 is a year value, 2 or 02 is a day.
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now)
fmt.Printf("Date only: %v\n", now.Format("2006/1/2"))
fmt.Printf("Date only: %v\n", now.Format("2006/01/02"))
fmt.Printf("Time only: %v\n", now.Format(">>15|04<<"))
fmt.Printf("Seconds: %v\n", now.Format("5"))
fmt.Printf("Locale: %v\n", now.Format("Z07-00"))
fmt.Printf("Kitchen time: %v\n", now.Format("PM <3>|<04>"))
}
// Output:
// 2022-04-15 09:40:59.471107 +0500 +05 m=+0.000071834
// Date only: 2022/4/15
// Date only: 2022/04/15
// Time only: >>09|40<<
// Seconds: 59
// Locale: +05-00
// Kitchen time: AM <9>|<40>
From string to time
The reverse process (getting time from a string) is pretty straightforward. To convert a string to a time value, the time package contains a function called Parse.
The Parse function takes two string-type arguments: layout, which is a certain time template, and value, which contains the string we want to convert. We can obtain the template for layout from constants or generate it ourselves. Parse returns two values: time.Time and error.
We get an error if the process of parsing ends with one of the following problems:
- A string contains invalid characters;
- A template and the input string have different layouts.
package main
import (
"fmt"
"time"
)
func main() {
source := "1969-12-28"
LinusTime, err := time.Parse("2006-01-02", source)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(LinusTime)
}
LinusTime, err = time.Parse(time.RFC3339, source)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(LinusTime)
}
}
// Output:
// 1969-12-28 00:00:00 +0000 UTC
// parsing time "1969-12-28" as "2006-01-02T15:04:05Z07:00": cannot parse "" as "T"
The first call to time.Parse() runs without errors, however, the second one returns an error message. This happens because we're trying to convert the string "1969-12-28", which doesn't properly follow the RFC3339 layout ("2006-01-02T15:04:05Z07:00"). As a result, we are getting the error message explaining that the source string is missing the T character!
Conclusion
Along with time units, months, and timezones, the time package contains constants for weekdays and layout templates. You can represent time in Go by using:
- The output of the
time.Date()function; - The output of the functions that get information about time units (
Year(),Month(),Day(),Hour(),Minute(),Second(),Nanosecond(),Location()); - The function
Format()with the desired template (from time constants or a format you created yourself).
Finally, to convert a string to a time.Time value, you need to use the Parse() function.
Now let's practice what you've learned in this topic!