【轉】理解Golang的Time結構

 

原文:https://www.jianshu.com/p/480ac51a22c0

--------------------

 

 

理解Golang的Time結構

0.2852018.10.26 18:49:54字數 845閱讀 2,340
 

在golang中創建並打印一個時間對象,會看到如下輸出

2018-10-26 14:15:50.306558969 +0800 CST m=+0.000401093

前面表示的意義好理解,分別是年月日和時間時區,最後的m=+xxxx這部分代表什麼呢?

Monotonic Clocks 和 Wall Clocks

根據golang的time包的文檔可以知道,golang的time結構中存儲了兩種時鐘,一種是Wall Clocks,一種是Monotonic Clocks。

Wall Clocks,顧名思義,表示牆上掛的鍾,在這裏表示我們平時理解的時間,存儲的形式是自 1970 年 1 月 1 日 0 時 0 分 0 秒以來的時間戳,當系統和授時服務器進行校準時間時間操作時,有可能造成這一秒是2018-1-1 00:00:00,而下一秒變成了2017-12-31 23:59:59的情況。Monotonic Clocks,意思是單調時間的,所謂單調,就是隻會不停的往前增長,不受校時操作的影響,這個時間是自進程啓動以來的秒數。

如果每隔一秒生成一個Time並打印出來,就會看到如下輸出。

2018-10-26 14:15:50.306558969 +0800 CST m=+0.000401093
2018-10-26 14:15:51.310559881 +0800 CST m=+1.004425285
2018-10-26 14:15:52.311822486 +0800 CST m=+2.005711106
2018-10-26 14:15:53.314599457 +0800 CST m=+3.008511329
2018-10-26 14:15:54.31882248 +0800 CST m=+4.012757636
2018-10-26 14:15:55.320059921 +0800 CST m=+5.014018292
2018-10-26 14:15:56.323814998 +0800 CST m=+6.017796644
2018-10-26 14:15:57.324858749 +0800 CST m=+7.018863606
2018-10-26 14:15:58.325164174 +0800 CST m=+8.019192224
2018-10-26 14:15:59.329058535 +0800 CST m=+9.023109863
2018-10-26 14:16:00.329591268 +0800 CST m=+10.023665796

可以看到m=+後面所顯示的數字,就是文檔中所說的Monotonic Clocks。

Time結構

那麼Monotonic Clock和Wall Clock在Time中是怎麼存儲的呢?來看一下Time結構體。

type Time struct {
    wall uint64
    ext  int64
    loc *Location
}

Time結構體中由三部分組成,loc比較明瞭,表示時區,wall和ext所存儲的信息規則相對複雜,根據文檔的介紹總結成了下圖:

 

golang中的Time結構,不像很多語言保存Unix時間戳(也就是最早只能表示到1970年1月1日),而是至少可以安全的表示1885年以來的時間。

t, _ := time.Parse(time.RFC3339, "1890-01-02T15:04:05Z")
fmt.Println(t) // 1890-01-02 15:04:05 +0000 UTC

實踐中需要注意的問題

既然Time結構所表示的時間,有可能有Monotonic Clock也可能沒有,那麼在使用中就有可能遇到一些問題,例如下面這種情況。

now := time.Now()
encodeNow, _ := json.Marshal(now)

decodeNow := time.Time{}
json.Unmarshal(encodeNow, &decodeNow)

fmt.Println(now)  // 2018-10-26 16:04:55.230121766 +0800 CST m=+0.000520419
fmt.Println(decodeNow)  // 2018-10-26 16:04:55.230121766 +0800 CST

可以看到,經過JSON轉碼之後,Time結構體會被表示成不帶Monotonic Clock的字符串,丟失了Monotonic Clock信息,而將字符串轉碼回Time結構時,自然也就和轉碼之前的不一樣了。同樣的情況,也發生在數據庫存儲中,存儲到數據庫裏的Time結構和從數據庫取出來的也是不一樣的。

當調用Equal比較兩個Time時,只有兩個Time都含有Monotonic Clock時,纔會根據Monotonic Clock比較大小,其他情況只比較Wall Clock部分。

timeA := time.Now()
timeB := time.Unix(0, timeA.UnixNano())

fmt.Println(timeA)  // 2018-10-26 16:37:02.216165074 +0800 CST m=+0.000363156
fmt.Println(timeB)  // 2018-10-26 16:37:02.216165074 +0800 CST

r := timeA.Equal(timeB)
fmt.Println(r)  // true

上面兩個時間的Wall Clock部分相同,一個有Monotonic Clock一個沒有,但是比較的結果是兩個時間是相同的。

timeA := time.Now()
timeB := time.Unix(timeA.Unix(), 0)

fmt.Println(timeA)  // 2018-10-26 16:38:25.653953438 +0800 CST m=+0.000364851
fmt.Println(timeB)  // 2018-10-26 16:38:25 +0800 CST

r := timeA.Equal(timeB)
fmt.Println(r)  // false

需要注意的是Wall Clock並不是秒之前的部分,Wall Clock本身也可以精確到納秒級別,所以一個精確到納秒的時間和一個精確到秒的時間也是不同的。

對於Time中的Monotonic Clock,我們可以使用time.Round(0)方法將其消除掉,以實現和其他語言一致的行爲。

timeA := time.Now()
timeB := timeA.Round(0)

fmt.Println(timeA)  // 2018-10-26 16:43:03.799263739 +0800 CST m=+0.000357758
fmt.Println(timeB)  // 2018-10-26 16:43:03.799263739 +0800 CST

參考文章

https://golang.org/pkg/time/

點擊關注知乎專欄Golang私房菜

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章