As programmers, we often have to deal with time. In Go, the standard library time provides a corresponding capability.
This article will introduce some important functions and methods in the time library, hoping to help those children who need Baidu when they encounter Go time processing problems.
Coping with time zone issues
In programming, we often encounter the eight- hour time difference . This is caused by time zone differences, and in order to better address them, we need to understand several time definition criteria.
GMT (Greenwich Mean Time), Greenwich Mean Time. GMT, which calculates time based on the Earth's rotation and revolution, specifies that the sun passes the Royal Greenwich Observatory in the suburbs of London, England, at 12 noon each day. GMT is the former Universal Standard Time.
UTC (Coordinated Universal Time), Coordinated Universal Time. UTC is more accurate than GMT, and it calculates time based on atomic clocks. In cases where precision to the second is not required, consider UTC=GMT. UTC is the current Universal Time.
From the prime meridian of Greenwich, it is positive to the east and negative to the west. The world is divided into 24 standard time zones, and the difference between adjacent time zones is one hour.
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println(time.Now())
}
Mainland China uses the standard time of the East Eight time zone, namely Beijing time CST, China Standard Time.
$ go run main.go
2022-07-17 16:37:31.186043 +0800 CST m=+0.000066647
This is the result in the default time zone time.Now()
and will be noted in the print +0800 CST
.
Assuming we are in the Los Angeles time zone, what is the result?
$ TZ="America/Los_Angeles" go run main.go
2022-07-17 01:39:12.391505 -0700 PDT m=+0.000069514
It can be seen that the result at this time is the -0700 PDT
time, which is PDT (Pacific Daylight Time) Pacific Daylight Time. Due to the time zone difference, the time results of the two executions differ by 15 hours.
Note that when using Docker containers, the default time zone of the system is UTC time (time zone 0), which is eight hours different from the actual Beijing time we need. This is a classic scenario that causes the eight- hour time difference .
For strategies to deal with time zone issues, you can check the loading logic of the initLocal() function in src/time/zoneinfo_unix.go in detail. For example, it can be solved by specifying the environment variable TZ, modifying the /etc/localtime file, etc.
Because the time zone issue is very important, it is described in the first part of the article. Let's start to introduce the use of the time library.
time instant time.Time
The core object of the time library is the time.Time structure. It is defined as follows to represent the time of a certain instant.
type Time struct {
// wall and ext encode the wall time seconds, wall time nanoseconds,
// and optional monotonic clock reading in nanoseconds.
wall uint64
ext int64
loc *Location
}
In terms of time processing, computers mainly involve two kinds of clocks.
-
Wall time, also known as clock time, is used to indicate a specific date and time.
-
Monotonic clocks, which are always guaranteed to move forward, do not have the problem of dialing back the wall clock, so they are well suited for measuring durations.
The wall and ext fields are used to record wall clocks and monotonic clocks with nanosecond precision. The corresponding digits of the field are associated with the specific year, month, day, hour, minute, second and other information used to determine the time.
The loc field records the time zone location. When loc is nil, the default is UTC time.
Because time.Time is used to represent instants of time with nanosecond precision, programs should generally store and pass it as a value, not a pointer.
That is, in time variables or struct fields, we should use time.Time instead of *time.Time.
get time.Time
We can get the current local time through the Now function
func Now() Time {}
You can also use the Date function to obtain the specified time according to the time and time zone parameters such as year, month, day, etc.
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time {}
Convert Timestamp
In the computer world, January 1, 1970 UTC 0:0:0:0 is used as Unix time 0. The so-called time instant is converted into a Unix timestamp, that is, the number of seconds, microseconds, etc. elapsed from Unix time 0 to the specified instant is calculated.
func (t Time) Unix() int64 {} // 从 Unix 时间 0 经过的秒数
func (t Time) UnixMicro() int64 {} // 从 Unix 时间 0 经过的微秒数
func (t Time) UnixMilli() int64 {} // 从 Unix 时间 0 经过的毫秒数
func (t Time) UnixNano() int64 {} // 从 Unix 时间 0 经过的纳秒数
Get basic fields
t := time.Now()
fmt.Println(t.Date()) // 2022 July 17
fmt.Println(t.Year()) // 2022
fmt.Println(t.Month()) // July
fmt.Println(t.ISOWeek()) // 2022 28
fmt.Println(t.Clock()) // 22 21 56
fmt.Println(t.Day()) // 17
fmt.Println(t.Weekday()) // Sunday
fmt.Println(t.Hour()) // 22
fmt.Println(t.Minute()) // 21
fmt.Println(t.Second()) // 56
fmt.Println(t.Nanosecond())// 494313000
fmt.Println(t.YearDay()) // 198
duration time.Duration
The duration time.Duration is used to represent the elapsed time between two time instants time.Time. It represents nanosecond counts by int64, and the limit that can be represented is about 290 years.
// A Duration represents the elapsed time between two instants
// as an int64 nanosecond count. The representation limits the
// largest representable duration to approximately 290 years.
type Duration int64
In Go, duration is just a number in nanoseconds. If the duration is equal to 1000000000, it means 1 second or 1000 milliseconds or 1000000 microseconds or 1000000000 nanoseconds.
For example, two time instants time.Time values 1 hour apart, the duration time.Duration value between them is
1*60*60*1000*1000*1000
These duration constant values are defined in Go's time package
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)
At the same time, time.Duration provides a method to obtain the value of each time granularity
func (d Duration) Nanoseconds() int64 {} // 纳秒
func (d Duration) Microseconds() int64 {} // 微秒
func (d Duration) Milliseconds() int64 {} // 毫秒
func (d Duration) Seconds() float64 {} // 秒
func (d Duration) Minutes() float64 {} // 分钟
func (d Duration) Hours() float64 {} // 小时
time calculation
After learning about time instants and durations, let's see how to do time calculations.
func (t Time) Add(d Duration) Time {}
-
The Add function is used to increase/decrease (a positive value of d means increase, a negative value means decrease) the duration of time.Time. We can increase or decrease a specified time in nanoseconds or more for an instantaneous time.
func (t Time) Sub(u Time) Duration {}
-
The Sub function can derive the duration between two instants of time.
func (t Time) AddDate(years int, months int, days int) Time {}
-
The AddDate function increments/decrements the value of time.Time based on the dimensions of year, month and day.
Of course, calculations based on the current time instant time.Now() are the most common needs. Therefore, the time package also provides the following convenient time calculation functions.
func Since(t Time) Duration {}
The Since function is a shortcut for time.Now().Sub(t).
func Until(t Time) Duration {}
The Until function is a shortcut for t.Sub(time.Now()).
Example of use
t := time.Now()
fmt.Println(t) // 2022-07-17 22:41:06.001567 +0800 CST m=+0.000057466
//时间增加 1小时
fmt.Println(t.Add(time.Hour * 1)) // 2022-07-17 23:41:06.001567 +0800 CST m=+3600.000057466
//时间增加 15 分钟
fmt.Println(t.Add(time.Minute * 15))// 2022-07-17 22:56:06.001567 +0800 CST m=+900.000057466
//时间增加 10 秒钟
fmt.Println(t.Add(time.Second * 10))// 2022-07-17 22:41:16.001567 +0800 CST m=+10.000057466
//时间减少 1 小时
fmt.Println(t.Add(-time.Hour * 1)) // 2022-07-17 21:41:06.001567 +0800 CST m=-3599.999942534
//时间减少 15 分钟
fmt.Println(t.Add(-time.Minute * 15))// 2022-07-17 22:26:06.001567 +0800 CST m=-899.999942534
//时间减少 10 秒钟
fmt.Println(t.Add(-time.Second * 10))// 2022-07-17 22:40:56.001567 +0800 CST m=-9.999942534
time.Sleep(time.Second * 5)
t2 := time.Now()
// 计算 t 到 t2 的持续时间
fmt.Println(t2.Sub(t)) // 5.004318874s
// 1 年之后的时间
t3 := t2.AddDate(1, 0, 0)
// 计算从 t 到当前的持续时间
fmt.Println(time.Since(t)) // 5.004442316s
// 计算现在到明年的持续时间
fmt.Println(time.Until(t3)) // 8759h59m59.999864s
format time
In other languages, a common time template is used to format the time. For example Python, which uses %Y for year, %m for month, %d for day, etc.
However, Go is different, it uses a fixed time (note that it is not possible to use other times) as a layout template, and this fixed time is the birth time of the Go language.
Mon Jan 2 15:04:05 MST 2006
Formatting time involves two conversion functions
func Parse(layout, value string) (Time, error) {}
-
The Parse function is used to convert a time string into a time.Time object according to the layout it can correspond to.
func (t Time) Format(layout string) string {}
-
The Formate function is used to convert a time.Time object to a time string according to the given layout.
Example
const (
layoutISO = "2006-01-02"
layoutUS = "January 2, 2006"
)
date := "2012-08-09"
t, _ := time.Parse(layoutISO, date)
fmt.Println(t) // 2012-08-09 00:00:00 +0000 UTC
fmt.Println(t.Format(layoutUS)) // August 9, 2012
In the time library, Go provides some predefined layout template constants, which can be used directly.
const (
Layout = "01/02 03:04:05PM '06 -0700" // The reference time, in numerical order.
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" // RFC822 with numeric zone
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" // RFC1123 with numeric zone
RFC3339 = "2006-01-02T15:04:05Z07:00"
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
Kitchen = "3:04PM"
// Handy time stamps.
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"
)
Below is a comparison table of our optional layout parameters
年 06/2006
月 01/1/Jan/January
日 02/2/_2
星期 Mon/Monday
小时 03/3/15
分 04/4
秒 05/5
毫秒 .000/.999
微秒 .000000/.999999
纳秒 .000000000/.999999999
am/pm PM/pm
时区 MST
时区小时数差-0700/-07/-07:00/Z0700/Z07:00
time zone conversion
At the beginning of the article, we introduced the time zone issue. If in the code, we need to get the results of the same time.Time in different time zones, we can use its In method.
func (t Time) In(loc *Location) Time {}
Its use is very simple, just look at the sample code directly
now := time.Now()
fmt.Println(now) // 2022-07-18 21:19:59.9636 +0800 CST m=+0.000069242
loc, _ := time.LoadLocation("UTC")
fmt.Println(now.In(loc)) // 2022-07-18 13:19:59.9636 +0000 UTC
loc, _ = time.LoadLocation("Europe/Berlin")
fmt.Println(now.In(loc)) // 2022-07-18 15:19:59.9636 +0200 CEST
loc, _ = time.LoadLocation("America/New_York")
fmt.Println(now.In(loc)) // 2022-07-18 09:19:59.9636 -0400 EDT
loc, _ = time.LoadLocation("Asia/Dubai")
fmt.Println(now.In(loc)) // 2022-07-18 17:19:59.9636 +0400 +04
Summarize
Overall, the time processing functions and methods provided by the time library basically meet our needs.
Interestingly, the Go time format conversion must adopt the birth time of Go, which is indeed narcissistic enough.