这些关于 Golang timezone 时区的坑, 我已经帮你踩过了
原文链接:
https://tangx.in/posts/2023/01/09/golang-timezone-issue/
Golang 中一些不太注意的时区问题
1. time/tzdata
库
Golang 内置的一个时区文件。
- 可以在程序中任意位置被导入。 导入后, 如果程序 找不到本地 时区文件, 就会使用该库的数据。
- 本地 指的是 运行环境, 可能是实际的服务器, 也可能是容器。
- 通常, 应该在
main.go
中被导入。 如果是 库代码 则 不应该 导入该文件。 - 导入该文件后, 程序会增加 450KB 大小。
1
2
3
| import (
_ "time/tzdata"
)
|
在老版本(1.15)以前并不包含时区信息, 通常会在容器化的时候单独处理时区问题。
1
2
3
4
5
6
7
8
9
10
11
| FROM golang:alpine as build
RUN apk --no-cache add tzdata
WORKDIR /app
ADD . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp
FROM scratch as final
COPY --from=build /app/myapp .
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
ENV TZ=Europe/Berlin
CMD ["/myapp"]
|
2. time.Parse
and time.ParseInLocation
- 使用
time.Parse
解析时间, 默认 时区是 UTC
。 - 使用
time.ParseInLocation
解析时间, 可以指定时区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| func timeStr2Time() {
timeStr := "2023-01-09 22:13:17"
// 返回的是UTC时间
// 2023-01-09 22:13:17 +0000 UTC
utcTimeObj, err := time.Parse("2006-01-02 15:04:05", timeStr)
if err == nil {
fmt.Println("time.Parse", utcTimeObj, utcTimeObj.Unix())
}
// 返回的是当地时间
// 2023-01-09 22:13:17 +0800 CST
localTimeObj, err := time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local)
if err == nil {
fmt.Println("time.ParseLocation", localTimeObj, localTimeObj.Unix())
}
}
|
3. 内部时区管理
- 默认情况下, 程序使用 程序运行的本地时区
- Go提供了两个函数快速转换 时区
time.UTC()
time.Local()
- 使用
LoadLocation(name)
设置时区。 - 使用
In(loc)
使用时区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| func setTimezone() {
n1.UTC() // 转换为 UTC 时区
n1.Local() // 转换为 本地时区
n1.Location() // 返回当前时间时区
// 没怎么用过
// loc := time.LoadLocationFromTZData()
loc2, _ := time.LoadLocation("Asia/Shanghai")
n1 := time.Now().In(loc2)
fmt.Println(n1)
}
|
4. 通过代码设置时区
在 实践操作 中 不允许 通过 代码 为 程序 本身设置时区的。
上面提到的 不允许, 说明
- 行为上 可以通过代码设置时区。
- 事实上 无法控制结果。
4.1 通过环境变量设置时区
可以通过设置 环境变量 的方式, 设置程序时区。
1
2
3
4
5
6
7
8
| // $ date # 本地时区 CST
// Mon Jan 9 23:43:03 CST 2023
func setTimezone() {
os.Setenv("TZ", "UTC")
fmt.Println(time.Now())
// 2023-01-09 15:42:51.309248 +0000 UTC m=+0.000084251
}
|
但是, 之后就 再也无法改变 时区了。
1
2
3
4
5
6
7
8
9
10
11
12
| func setTimezone() {
os.Setenv("TZ", "UTC")
fmt.Println(time.Now())
os.Setenv("TZ", "Asia/Shanghai")
fmt.Println(time.Now())
}
// 注意看第二个时间也是 UTC 时间
// 2023-01-09 15:46:41.130211 +0000 UTC m=+0.000180001
// 2023-01-09 15:46:41.130284 +0000 UTC m=+0.000253042
|
不仅如此, 如果之前执行过时间命令, 那么 即使第一次设置 时区也是无效的。
1
2
3
4
5
6
7
8
9
10
11
12
|
func setTimezone() {
fmt.Println(time.Now())
os.Setenv("TZ", "UTC")
fmt.Println(time.Now())
}
// 2023-01-09 23:48:52.72857 +0800 CST m=+0.000167418
// 2023-01-09 23:48:52.729103 +0800 CST m=+0.000696960
|
总结, 时间操作的顺序会 影响 时区的设置。
设置时区
到目前为止, 我还是只能老老实实去 运行环境 中操作, 设置 环境变量 TZ
。
1
| export TZ=Asia/Shanghai
|
具体可以参考
在容器中设置时区原来这么简单
。
除此之外, 还没找到其它好的办法。