1. 時間與時區(qū)
1.1 時間標準
UTC,世界標準時間,是現(xiàn)在的時間標準,以原子時計時。
GMT,格林威治時間,是以前的時間標準,規(guī)定太陽每天經(jīng)過位于英國倫敦郊區(qū)的皇家格林威治天文臺的時間為中午 12 點。
UTC 時間更加準確,但如果對精度要求不高,可以視兩種標準等同。
1.2 時區(qū)劃分
從格林威治本初子午線起,經(jīng)度每向東或者向西間隔 15°,就劃分一個時區(qū),因此一共有 24 個時區(qū),東、西個 12 個。
但為了行政上的方便,通常會將一個國家或者一個省份劃分在一起。下面是幾個 UTC 表示的時間:
- UTC-6(CST — 北美中部標準時間)
- UTC+9(JST — 日本標準時間)
- UTC+8(CT/CST — 中原標準時間)
- UTC+5:30(IST — 印度標準時間)
- UTC+3(MSK — 莫斯科時區(qū))
1.3 Local 時間
Local 時間為當前系統(tǒng)的帶時區(qū)時間,可以通過 /etc/localtime 獲取。實際上 /etc/localtime 是指向 zoneinfo 目錄下的某個時區(qū)。下面是 MacOS 上的執(zhí)行結(jié)果,Linux 上的路徑會不一樣:
1
2
3
|
ls -al /etc/localtime lrwxr-xr-x 1 root wheel 39 Apr 26 2021 /etc/localtime -> /var/db/timezone/zoneinfo/Asia/Shanghai |
2. Go 中的時間及序列化
2.1 Go 如何初始化時區(qū)
- 查找 TZ 變量獲取時區(qū)
- 如果沒有 TZ,那么使用 /etc/localtime
- 如果 TZ="",那么使用 UTC
- 當 TZ=“foo” 或者 TZ=":foo"時,如果 foo 指向的文件將被用于初始化時區(qū),否則使用 /usr/share/zoneinfo/foo
下面是 Go 實現(xiàn)的源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
tz, ok := syscall.Getenv( "TZ" ) switch { case !ok: z, err := loadLocation( "localtime" , []string{ "/etc" }) if err == nil { localLoc = *z localLoc.name = "Local" return } case tz != "" : if tz[ 0 ] == ':' { tz = tz[ 1 :] } if tz != "" && tz[ 0 ] == '/' { if z, err := loadLocation(tz, []string{ "" }); err == nil { localLoc = *z if tz == "/etc/localtime" { localLoc.name = "Local" } else { localLoc.name = tz } return } } else if tz != "" && tz != "UTC" { if z, err := loadLocation(tz, zoneSources); err == nil { localLoc = *z return } } } |
2.2 Go 時間字段的序列化
在 Go 使用 “encoding/json” 可以對 Time 字段進行序列化,使用 Format 可以對時間格式進行自定義。如下示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package main import ( "encoding/json" "fmt" "time" ) func main(){ fmt.Println(time.Now()) var a, _ := json.Marshal(time.Now()) fmt.Println(string(a)) a, _ = json.Marshal(time.Now().Format(time.RFC1123)) fmt.Println(string(a)) a, _ = json.Marshal(time.Now().Format( "06-01-02" )) fmt.Println(string(a)) } |
輸出結(jié)果:
2021-12-07 16:44:44.874809 +0800 CST m=+0.000070010
"2021-12-07T16:44:44.874937+08:00"
"Tue, 07 Dec 2021 16:44:44 CST"
"00-120-74 16:44:07"
"21-12-07"
2.3 Go 結(jié)構(gòu)體中的時間字段序列化
在結(jié)構(gòu)體中,如果直接使用 “encoding/json” 對結(jié)構(gòu)體進行序列化,得到的將會是這樣的時間格式: 2021-12-07T17:31:08.811045+08:00。無法使用 Format 函數(shù)對時間格式進行控制。
那么,如何控制結(jié)構(gòu)體中的時間格式呢?請看如下示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
|
package main import ( "fmt" "strings" "time" "unsafe" "encoding/json" jsoniter "github.com/json-iterator/go" ) func main() { var json2 = NewJsonTime() var d = struct { Title string `json: "title" ` StartedAt time.Time `json: "time" ` }{ Title: "this is title" , StartedAt: time.Now(), } t1, _ := json.Marshal(d) fmt.Println(string(t1)) t2, _ := json2.Marshal(d) fmt.Println(string(t2)) } func NewJsonTime() jsoniter.API { var jt = jsoniter.ConfigCompatibleWithStandardLibrary jt.RegisterExtension(&CustomTimeExtension{}) return jt } type CustomTimeExtension struct { jsoniter.DummyExtension } func (extension *CustomTimeExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) { for _, binding := range structDescriptor.Fields { var typeErr error var isPtr bool name := strings.ToLower(binding.Field.Name()) if name == "startedat" { isPtr = false } else if name == "finishedat" { isPtr = true } else { continue } timeFormat := time.RFC1123Z locale, _ := time.LoadLocation( "Asia/Shanghai" ) binding.Encoder = &funcEncoder{fun: func(ptr unsafe.Pointer, stream *jsoniter.Stream) { if typeErr != nil { stream.Error = typeErr return } var tp *time.Time if isPtr { tpp := (**time.Time)(ptr) tp = *(tpp) } else { tp = (*time.Time)(ptr) } if tp != nil { lt := tp.In(locale) str := lt.Format(timeFormat) stream.WriteString(str) } else { stream.Write([] byte ( "null" )) } }} binding.Decoder = &funcDecoder{fun: func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { if typeErr != nil { iter.Error = typeErr return } str := iter.ReadString() var t *time.Time if str != "" { var err error tmp, err := time.ParseInLocation(timeFormat, str, locale) if err != nil { iter.Error = err return } t = &tmp } else { t = nil } if isPtr { tpp := (**time.Time)(ptr) *tpp = t } else { tp := (*time.Time)(ptr) if tp != nil && t != nil { *tp = *t } } }} } } type funcDecoder struct { fun jsoniter.DecoderFunc } func (decoder *funcDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { decoder.fun(ptr, iter) } type funcEncoder struct { fun jsoniter.EncoderFunc isEmptyFunc func(ptr unsafe.Pointer) bool } func (encoder *funcEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { encoder.fun(ptr, stream) } func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool { if encoder.isEmptyFunc == nil { return false } return encoder.isEmptyFunc(ptr) } |
輸出結(jié)果:
{"title":"this is title","time":"2021-12-07T17:31:08.811045+08:00"}
{"title":"this is title","time":"Tue, 07 Dec 2021 17:31:08 +0800"}
這里主要是使用 “github.com/json-iterator/go” 包控制 Go 對時間字段的序列化,通過其提供的擴展指定 key 為 startedat、finishedat 的時間字段,指定序列化時使用 timeFormat := time.RFC1123Z 格式和 locale, _ := time.LoadLocation("Asia/Shanghai") 時區(qū)。
3. 各種環(huán)境下設置時區(qū)
3.1 在 Linux 中
執(zhí)行命令:
1
|
timedatectl set -timezone Asia /Shanghai |
或者設置 TZ 環(huán)境變量:
1
2
3
|
TZ= 'Asia/Shanghai' export TZ |
都可以設置時區(qū)。
3.1 在 Docker 中
在制作鏡像時,直接在 Dockerfile 設置 TZ 變量,可能會碰到問題:
1
2
3
4
5
|
FROM alpine ENV TZ= 'Asia/Shanghai' COPY . /time .go . |
報錯: panic: time: missing Location in call to Time.In
原因: 我們常用的 Linux 系統(tǒng),例如 Ubuntu、CentOS,在 /usr/share/zoneinfo/ 目錄下存放了各個時區(qū)而 alpine 鏡像沒有。
因此 alpine 鏡像需要安裝一些額外的包。
1
2
3
4
5
6
7
8
9
|
FROM alpine RUN apk add tzdata && \ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ echo "Asia/Shanghai" > /etc/timezone |
在運行容器時,可以直接掛載主機的時區(qū)描述文件:
1
|
docker run -it -- rm - v /etc/localtime : /etc/localtime :ro nginx |
3.2 在 Kubernetes 中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
apiVersion: v1 kind: Pod metadata: name: test namespace: default spec: restartPolicy: OnFailure containers: - name: nginx image: nginx- test imagePullPolicy: IfNotPresent volumeMounts: - name: date -config mountPath: /etc/localtime command : [ "sleep" , "60000" ] volumes: - name: date -config hostPath: path: /etc/localtime |
這里將主機上的時區(qū)文件掛載到 Pod 中。
4. 參考
https://github.com/json-iterator/go
5.golang時區(qū)處理
如果要設定時區(qū),那么在使用時間函數(shù)之前,就要設定時區(qū)。
那么問題就來了,打個比喻說。我想在墨西哥5月6號12點45分時開始促銷。而我在中國,那么你要設定了個什么樣的數(shù)字呢?
墨西哥是西5時區(qū)-5,中國是+8時區(qū),相差13個時區(qū),也就是在中國今天是5.6號,那么墨西哥是5.5號
也就是說,我今天要設置5.7號的時間嗎?
。。。。。。。。。。。。。
其實我覺得,是不是直接設定5.6號就行了。因為設定了,那么墨西哥是5.6號做的促銷,你只要在5.7號跟進就行了。
如果你想要看交易數(shù)據(jù)(按照中國的時間來看),那樣才要做轉(zhuǎn)換。也就是中國時間5.7號,墨西哥賣出了多少貨。
好了,不扯蛋了。下面是有需要轉(zhuǎn)時區(qū)的寫法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
var cstZone = time .FixedZone( "CST" , -7*3600) //設定要轉(zhuǎn)換的時區(qū)<br> <br> h,:=time.ParseDuration("-1h") //中國的時間是+8區(qū) // element t,err:= time .Parse( "2006-01-02 15:04:05" ,item.SaleStartTime) //要處理的時間格式,使用入的字符串要跟格式化的一致 var tString string if err!=nil{ tString= time .Now().In(cstZone).Format( "2006-01-02T15:04:05-0700" ) // 這時有個坑,不需要的自己想加法解決 } else {<br> t=t.Add(8*h) //要減去+8區(qū) tString=t.In(cstZone).Format( "2006-01-02T15:04:05-0700" ) // 使用時區(qū)轉(zhuǎn)化為對應國家的時間。小心格式化的時間,填自己想要的格式。 } |
總結(jié)
到此這篇關于Go中時間與時區(qū)問題的文章就介紹到這了,更多相關Go時間與時區(qū)問題內(nèi)容請搜索服務器之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://www.chenshaowen.com/blog/the-tips-of-time-and-tz-in-go.html