[Go 入门] 第二十二章 Go 语言时间编程

Go 入门系列参考于互联网资料与 人民邮电出版社 《Go 语言入门经典》 与 《Effective Go》,编写目的在于学习交流,如有侵权,请联系删除

本文内容:

时间元素编程

时间是一个重要的编程元素,可用于计算、同步服务器以及测量。Go 语言标准库提供了 time 包,其中包含用于同当前时间交互以及测量时间的函数和方法

在编程中,时间通常被成为“实时”、“过去的时间”和“壁挂钟”。对于术语“壁挂钟”,可将其视为挂在墙上的时钟。它显示的时间随时区或地区性调整而异,但每隔 24h 显示的时间都相同。对编程来说,这种随时区、地区性调整而异的二十四小时制并不理想,因此也存在单调时钟(monotonic clock)的概念。单调时钟以固定的方式度量时间,可用于可靠地度量事件发生的时间

要使用 Go 语言打印计算中的当前的时间,可使用函数 Now

1
2
3
func main() {
fmt.Println(time.Now())
}

随后执行输出:

1
2020-02-03 20:05:02.1266241 +0800 CST m=+0.001996801

根据计算机地区的设置,看到的打印出来的时间格式可能不同

网络时间协议 (Network Time Protocol, NTP)是一种在整个网络中同步时间的网络协议,使用 NTP 的不同计算机更有可能就时间达成一致,但在本地它们依然可以设置成不同的时区

在计算机中,要消除时区的影响,可参照世界标准时间 (Coordinated Universal Time, UTC)。UTC 是时间标准而非时区,它让不同地区的计算机有相同的参照物,而不用考虑相对时区

让程序休眠

知道时间编程的复杂性后,该让程序休眠了。在计算程序中,休眠意味着暂停执行程序。在休眠期间,程序什么都不做。在 Go 语言中,如果 Goroutine 处于休眠状态,则程序的其他部分可继续执行。

1
2
3
4
func main() {
time.Sleep(3 * time.Second)
fmt.Println("I'm awake")
}

为了让程序暂停执行一会儿,休眠是很有用的。可通过休眠来等待其他执行任务完成或让程序暂停执行。但除非只是想让程序暂停一会,否则使用 Goroutine 来管理执流程是更佳的选择

设置超时时间

要在特定的时间过后执行某项操作,可使用函数 After

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
"time"
)

func main() {
fmt.Println("You have two seconds to calculate 19 *4")
for {
select {
case <- time.After(2 * time.Second):
fmt.Println("Time's up!")
return
}
}
}

使用 ticker

使用 ticker 可让代码每隔特定的时间就重复执行一次。需要在很长的时间内,定期执行任务时,这么做很有用

1
2
3
4
5
6
func main() {
c := time.Tick(5 * time.Second)
for t := range c {
fmt.Printf("The time is now %v\n", t)
}
}

执行后将每 5s 输出当前的时间:

1
2
3
4
5
6
7
8
The time is now 2020-02-03 20:19:11.1405347 +0800 CST m=+5.002022601
The time is now 2020-02-03 20:19:16.1413798 +0800 CST m=+10.002867701
The time is now 2020-02-03 20:19:21.1405194 +0800 CST m=+15.002007301
The time is now 2020-02-03 20:19:26.1406661 +0800 CST m=+20.002154001
The time is now 2020-02-03 20:19:31.1421992 +0800 CST m=+25.003687101
The time is now 2020-02-03 20:19:36.1407473 +0800 CST m=+30.002235201
The time is now 2020-02-03 20:19:41.1423582 +0800 CST m=+35.003846101
The time is now 2020-02-03 20:19:46.140615 +0800 CST m=+40.002102901

以字符串格式表示时间

时间的字符串表示

类型 字符串
ANSIC MON Jan _2 15:04:05 2006
UnixDate MON Jan _2 15:04:05 MST 2006
RubyDate MON Jan _2 15:04:05 -0700 2006
RFC822 02 Jan 06 15:04 MST
RFC822Z 02 Jan 06 15:04 -0700
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
RFC3339 2006-01-02T15:04:05Z07:00
RFC3339Nano 2006-01-02T15:04:05.999999999Z07:00

通常,使用上述标准将日期储存在数据库中,并以字符串的方式提供它们。 Go 语言支持时间标准,也支持定义不符合这些标准的时间格式

1
2
3
4
5
6
7
8
func main() {
s := "2006-01-02T15:04:05.999999999Z"
t, err := time.Parse(time.RFC3339Nano, s)
if err != nil{
log.Fatal(err)
}
fmt.Println(t)
}

上述代码将一个 RFC3339Nano 格式的时间字符串传递给了结构体 Time, 再将这个结构体打印到终端

2006-01-02 15:04:05.999999999 +0000 UTC

使用结构体 Time

1
2
3
4
5
6
7
8
9
10
11
func main() {
s := "2006-01-02T15:04:05.999999999Z"
t, err := time.Parse(time.RFC3339Nano, s)
if err != nil{
log.Fatal(err)
}
fmt.Println(t.Hour())
fmt.Println(t.Minute())
fmt.Println(t.Second())
fmt.Println(t.Date())
}

执行后将会输出:

1
2
3
4
15
4
5
2006 January 2

时间加减

方法 Add 在既有时间的基础上加上一定时间,可将其结果赋给变量

1
2
3
4
5
6
7
8
9
func main() {
s := "2006-01-02T15:04:05.999999999Z"
t, err := time.Parse(time.RFC3339Nano, s)
if err != nil{
log.Fatal(err)
}
nt := t.Add(2 * time.Hour)
fmt.Println(nt)
}

方法 Sub 从既有时间中减去指定的时间,也可以将其结果赋给变量

1
2
3
4
5
6
7
8
9
func main() {
s := "2006-01-02T15:04:05.999999999Z"
t, err := time.Parse(time.RFC3339Nano, s)
if err != nil{
log.Fatal(err)
}
nt := t.Sub(2 * time.Hour)
fmt.Println(nt)
}

比较两个不同的 Time 结构体

time 包提供了方法 BeforeAfterEqual,这些方法来比较两个时间结构体,并返回一个布尔值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main() {
s1 := "2020-02-03T15:04:05Z"
s2 := "2020-02-04T15:04:05Z"
today, err := time.Parse(time.RFC3339, s1)
if err != nil{
log.Fatal(err)
}
tomorrow, err := time.Parse(time.RFC3339, s2)
if err != nil{
log.Fatal(err)
}
fmt.Println(tomorrow.After(today))
fmt.Println(tomorrow.Before(today))
fmt.Println(tomorrow.Equal(today))
}

运行上述代码,将输出:

1
2
3
true
false
false

问题列表

  • 该使用那种时间格式

    UTC 使用的是 ISO 8601 格式,这种格式在网上得到了很好的支持,RFC3339 是一个 ISO 8601 扩展,这两种标准都是不错的选择,如果有疑问,可使用得到广泛支持的 ISO 8601 标准

  • 该将 UTC 还是 GMT 作为网络时间

    当前,计算机和互联网都使用标准 UTC。GMT 实际上是一个时区,时区会时间调整的影响,而时间标准不会