This_is_Fine_Gopher.png
大家好,我叫謝偉,是一名程序員。
近期會更新內置庫的學習筆記,主要參考文獻來自於官方文檔和源代碼。
本節的主題:time
時間的操作在項目中使用的非常頻繁,比如說數據庫中,經常有時間的操作,比如根據時間進行劃分,統計之類的功能。
那麼如何學會常用的操作呢?
大綱:
- 自己總結的常用操作
- 官方的 API
- 學習總結
自己總結的常用操作
時間類型的數據,我們在項目中都會進行哪些操作呢?
要在思維裏掌握時間類型的數據的操作,又應該如何梳理呢?
1. 時間的單位
暫時撇開代碼層面,日常生活中關於時間的單位都有哪些呢?
- 年
- 月
- 周
- 日
- 時
- 分
- 秒
- 毫秒
一般到毫秒層面就夠了,當然還可以繼續劃分:微秒、納秒...
那時間的單位的轉換是如何進行的呢?
- 1 y = 12 m
- 1 m = 4 w
- 1 w = 7 d
- 1 d = 24 h
- 1 h = 60 min
- 1 min = 60 s
- 1 s = 1000 ms
- 1 ms = 1000 us
- 1 us = 1000 ns
爲什麼要了解這些啊? 這不是常識嗎?是的。經常有常識性的問題,轉換成代碼層面而出錯。所以有必要進行了回顧。
2. 時間操作
瞭解了時間的基本單位,那就好辦了。
func main(){ now := time.Now() fmt.Println(now.Year()) fmt.Println(now.Month()) fmt.Println(now.Day()) fmt.Println(now.Hour()) fmt.Println(now.Minute()) fmt.Println(now.Second()) fmt.Println(now.Date()) } >> 2018-11-13 22:30:03.500763 +0800 CST m=+0.000810351 2018 November 13 22 30 3 2018 November 13
所有這些時間的單位底層都是 int
類型。既然是 int
類型,那 November 是如何得到的?
很簡單,底層定義這樣一個關於月份的數組,將對應的位置的值返回即可。
var months = [...]string{ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", }
3. 時間戳
- 時間戳的使用也非常廣泛,比如爲了表示唯一性
- 時間戳的概念:1970年01月01日00時00分00秒(北京時間1970年01月01日08時00分00秒)起至現在的總秒數
時間戳,這個概念,如果不是程序員,可能完成沒有必要了解,有更好的閱讀時間的顯示方式,沒必要知道這麼一串數字。
在 Go 中,如何將時間轉換成時間戳?
func main(){ now := time.Now() fmt.Println(now.UnixNano()) fmt.Println(now.Unix()) } >> 1542119403501037000 1542119403
上文一個轉換爲納秒,一個是轉換爲秒。可以看到轉換單位是 10^9
那如何將時間戳轉換爲 時間類型?
func main(){ fmt.Println(time.Unix(1542119403, 1542119403000000000)) fmt.Println(time.Unix(1542119403, 1542119403000000000).UTC()) } >> 2067-09-26 13:00:06 +0800 CST 2067-09-26 05:00:06 +0000 UTC
關於時間,還存在一個重要的概念,即:時區
- UTC 校準的全球時間
- CST China Standard Time UT 8:00
所以經常會出現時間相差 8 小時的情況,比如,本地程序運行正常,推送到倉庫中,自動構建夠,程序得不到預期的值,有可能是因爲服務器的時間的時區和本地的不一致。
4. 時間和字符串的相互轉化
涉及時間的顯示的佈局有個默認值:2006-01-02 15:04:05
這個時間的速記:123456
func main(){ now := time.Now() fmt.Println(now.Format("2006-01-02 15:04:05")) } timeString := "2019-01-02 15:03:00" fmt.Println(time.Parse("2006-01-02 15:04:05", timeString)) fmt.Println(time.ParseInLocation("2006-01-02 15:04:05", timeString, time.Local)) >> 2018-11-13 22:59:08 2019-01-02 15:03:00 +0000 UTC <nil> 2019-01-02 15:03:00 +0800 CST <nil>
還記得之前字符串和基本數據類型(整型、浮點型、布爾型)相互轉化的章節。
我們總結:
- 基本數據類型轉化爲字符串:帶關鍵字 Format, 且沒有錯誤返回
- 字符串轉化爲基本數據類型:帶關鍵字 Parse , 且存在錯誤返回
時間類型和字符串之間的轉化和上文的總結一致。
啓發:使用關鍵字:Format 、 Parse 對自己函數進行命名組織;沒有錯誤類型的函數不返回錯誤類型
有時候我們可能只需要時間的部分值,比如說,只需要整點的值,那關於這些操作,又應該如何操作呢?
func main(){ timeToString() } var timeToString = func() { now := time.Now() fmt.Println(now.Format("2006-01-02 15:00:00")) fmt.Println(now.Format("2006-01-02 15:04:00")) fmt.Println(now.Format("2006-01-02 00:00:00")) fmt.Println(now.Round(time.Hour)) fmt.Println(now.Round(time.Minute)) fmt.Println(now.Round(time.Second)) fmt.Println(now.Truncate(time.Hour)) fmt.Println(now.Truncate(time.Minute)) fmt.Println(now.Truncate(time.Second)) } >> 2018-11-13 23:00:00 2018-11-13 23:12:00 2018-11-13 00:00:00 2018-11-13 23:00:00 +0800 CST 2018-11-13 23:13:00 +0800 CST 2018-11-13 23:12:34 +0800 CST 2018-11-13 23:00:00 +0800 CST 2018-11-13 23:12:00 +0800 CST 2018-11-13 23:12:33 +0800 CST
可以看出,存在兩種方法,一種是操作 佈局,即更改那個默認值 2006-01-02 15:04:05
; 一種是使用內置的 Round, Truncate 兩種的區別是Round 向上取整,Truncate 向下取整
5. 兩個時間之間的操作
上面的例子絕大多數是單個時間的操作,比如取時間戳、時間和字符串之間轉化、獲取時間的年月日等。
日常操作中,兩個時間的操作也是比較頻繁的。
比如:
- 判斷一個時間是否是在該時間之前、之後
- 給出給定時間的某個時間節點的值:比如 1小時前、1天前、1年前、1年後、1天后、1小時後等
func main(){ timeOp() } var timeOp = func() { now := time.Now() fmt.Println(now.Add(1 * time.Hour)) fmt.Println(now.Add(24 * time.Hour)) fmt.Println(now.Add(-10 * time.Hour)) } >> 2018-11-14 00:21:47.48055 +0800 CST m=+3600.000854277 2018-11-14 23:21:47.48055 +0800 CST m=+86400.000854277 2018-11-13 13:21:47.48055 +0800 CST m=-35999.999145723
得到 1小時後、1天后、10小時前
好,那這些時間前後都有哪些值可以選擇?
const ( Nanosecond Duration = 1 Microsecond = 1000 * Nanosecond Millisecond = 1000 * Microsecond Second = 1000 * Millisecond Minute = 60 * Second Hour = 60 * Minute )
這些常量,輔以恰當的運算。
那 1年前咋搞?還運算嗎?傻瓜。
func main(){ timeOpDate() } var timeOpDate = func() { now := time.Now() fmt.Println(now.AddDate(-1, 0, 0)) fmt.Println(now.AddDate(2, 0, 0)) fmt.Println(now.AddDate(0, 1, 0)) } >> 2017-11-13 23:26:18.697797 +0800 CST 2020-11-13 23:26:18.697797 +0800 CST 2018-12-13 23:26:18.697797 +0800 CST
完成啦。
基本上這兩個函數就可以完成目的。
那如何計算兩個時間之間的差呢?
func main(){ timeInterval() } var timeInterval = func() { now := time.Now() stringTime := "2018-11-14 20:00:00" newTime, _ := time.ParseInLocation("2006-01-02 15:04:05", stringTime, time.Local) if newTime.After(now) { subTime := newTime.Sub(now) fmt.Println("newTime after now") fmt.Println(subTime.Hours()) fmt.Println(subTime.Minutes()) fmt.Println(subTime.Seconds()) fmt.Println(subTime.Nanoseconds()) } if newTime.Before(now) { subTime := now.Sub(newTime) fmt.Println(subTime.String()) } } >> newTime after now 20.463746377777777 1227.8247826666666 73669.48696 73669486960000
6. 其他
- 得出時間是本週的周幾
- 得出時間是該年的第幾周
func main(){ fmt.Println(time.Now().ISOWeek()) fmt.Println(time.Now().Weekday()) } >> 2018 46 Tuesday
官方API
大概我們沒講有這些內容:
- 定時器?
- 底層是如何獲取時間的?
- 序列化和反序列化(還記得 json 那節 Marshler 嗎)
func (t Time) MarshalJSON() ([]byte, error) MarshalJSON implements the json.Marshaler interface. The time is a quoted string in RFC 3339 format, with sub-second precision added if present.
自定義了序列化的結構
總結
枚舉類型在 Go 裏面的時候,用來表示連續的遞增的值。
比如想表示顏色、比如像表示星期幾
這種連續的值,一般都選擇枚舉類型來定義。
const ( Sunday Weekday = iota Monday Tuesday Wednesday Thursday Friday Saturday ) const ( January Month = 1 + iota February March April May June July August September October November December )
比如想自定義的顏色順序:
const ( Red = iota Orange Yellow Green Cyan Blue Purple ) func main(){ fmt.Println(Red, Orange, Yellow, Green, Cyan, Blue, Purple) } >> 0 1 2 3 4 5 6
當然提到枚舉,考點不會這樣考你,而是如下面的操作:
const ( Red = iota Orange Yellow = iota + 10 Green = iota Cyan Blue = 10 Purple )
問你各個值得多少?
0 1 12 3 4 10 10
<完>