網站首頁 編程語言 正文
簡介
在各個語言之中都有時間類型的處理,因為這個地球是圓的(我仿佛在講廢話),有多個時區,每個時區的時間不一樣,在程序中有必要存在一種方式,或者說一種類型存儲時間,還可以通過一系列的方法轉換成不同國家的時間。
上問提到了時間、時區,還有一個概念為兩個時間之間的差值,比如小熊每次可以堅持1個小時(鍛煉),1個小時這種時間形容詞就是時間間隔。
這就是三種時間處理的類型。
類型
Time
、Location
、Duration
時間、時區、時間間隔。它們都在time
包里面。
Time時間類型
程序中應使用 Time
類型值來保存和傳遞時間,一個結構體,精確到納秒。里面的變量都是私有的用不到,先不去管他。
type Time struct { sec int64 //秒 nsec int32 //納秒 loc *Location //時區 }
- 一個
Time
類型值可以被多個go
程同時使用。因為它是time.Time
類型,而不是 指針*time.Time
類型。 - 時間需要初始化:
IsZero
方法提供了檢驗時間是否是顯式初始化。 - 時區類型作為
Time
結構體中的一個字段,標記這個時間當前是哪個時區。
Duration
?時間間隔,兩個時間之間的差值,以納秒為單位,最長 290
年,作為常識即可。
type Duration int64
時區
我們在使用time.Time類型一般都是Local時間,也就是本地時間,現在就是中國時間。
// 本地時間(如果是在中國,獲取的是東八區時間) curLocalTime := time.Now() // UTC時間 curUTCTime := time.Now().UTC()
time
包提供了 Location
(也就是時區)的兩個實例:Local
和 UTC
。
-
Local
代表當前系統本地時區;UTC
代表通用協調時間,也就是零時區。 -
time
包默認(為顯示提供時區)使用Local
時區。 - 平時使用的都是
Local
時間,數據庫存儲的時候要注意,一般orm
框架會自動實現這個。
默認就是Local中國時間!
問題:時區這個怎么設置?傳字符串進去嗎?
curLocalTime := time.Now() //這是local curUtcTime := curLocalTime.In(time.UTC) //這是UTC
時區特別容易出錯,Time
我們使用都是本地時間,但是!坑來了!
小心有坑
timeStr := "2022-01-13 22:32:17" utcTimeObj, err := time.Parse("2006-01-02 15:04:05", timeStr) if err == nil { fmt.Println(utcTimeObj, utcTimeObj.Unix()) }
你猜猜會輸出什么?返回的竟然是UTC
時間2022-01-13 22:32:17 +0000 UTC
。這個經常有人出錯。解析字符串時,都以協調時UTC
時間為準。
還有另一個辦法,比較穩。我們應該總是使用 time.ParseInLocation
來解析時間,并給第三個參數傳遞 time.Local
。
localTimeObj, err := time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local) if err == nil { fmt.Println(localTimeObj) }
它返回的是time 類型是嗎?沒錯!這兩個返回的都是time類型。
問:這個會用在哪個場景?
好問題,問到點子上了!
時間解析的使用場景
前后端傳輸json
數據的時候,或者數據庫存儲讀取的時候。前后端建議使用時間戳傳輸,不要使用時間字符串可以大大省心。數據庫如果使用orm的框架,一般是會自動處理時間存儲。
我們約定好用時間戳傳遞,總是有一些比較軸的同事一定要用字符串傳輸,你有沒有這樣的同事?如果非要使用字符串傳輸,在傳遞json的時候就需要反復的做解析相當的不友善。
但也不是不能做~~
大家了解過json解析和反解析有哪兩個方法嗎?有沒有人重寫過 UnmarshalJSON
和 MarshalJSON
。我們來復習一下。
書里面的提到在不同辦法的接口,有可能json
字段的類型會發生改變,一般做兼容性處理的時候會重寫到。
看這個截圖,字符串轉換成結構體,反過來結構體轉換成字符串,就是用MarshalJSON
。
type Person struct { Id int64 `json:"id"` Name string `json:"name"` Birthday Time `json:"_"` }
比如一個結構體,里面有一個時間類型,你的前端同事又不傳時間戳,你就得手動轉換成時間類型,或者時間戳,這個你自己決定。這里是 Birthday
舉例,我的注解里面用的json
是一個下劃線,在解析的時候就不會寫入。
問:這個不寫入, 是 json
庫實現的,還是自己實現的?
json
庫。json
庫讀取注解,匹配json
中的字段名稱,寫入到結構體中。我的注解里寫成了下劃線,這只是一個占位符,習慣上這么寫。你也可以寫成-
中杠線。
我先寫了一個People
的反解析函數,json.UnmarshalJSON
會嘗試調用。看截圖
- 先解析到匿名結構體變量中,
birthday
字段賦值給了s.Brithday
,其他字段給了s.tmp
-
s.Birthday
是一個字符串類型,再把這個類型轉換成時間類型。 - 把
localtime
放到tmp
里面,tmp
就是之前的people
。
所以返回的就是tmp
, 才是我們要的。
*p = People(s.tmp)
最后再創建一個People
,把tmp
傳遞過去。
【思考題】為什么這里還要創建一個,直接賦值s.tmp
給*p
可以不?(這里我給你們挖了一個坑)。
我定義的是新類型,并不是創建,實際上是一個強制類型轉換。哈哈哈,我就是蔫壞。
關于時間處理的各種函數我也列在下面了,大家收藏看就行了。還是剛剛提到的各種完整代碼。喜歡這篇文章的話點個在看,么么噠。
時間操作
獲取當前時間
import time func getCurTime() { // 本地時間(如果是在中國,獲取的是東八區時間) curLocalTime := time.Now() // UTC時間 curUTCTime := time.Now().UTC() fmt.Println(curLocalTime, curUTCTime) }
時區設置
不同國家(有時甚至是同一個國家內的不同地區)使用不同的時區。對于要輸入和輸出時間的程序來說,必須對系統所處的時區加以考慮。Go 語言使用 Location
來表示地區相關的時區,一個 Location 可能表示多個時區。展開講解 time
包提供了 Location 的兩個實例:Local
和 UTC
-
Local
代表當前系統本地時區;UTC
代表通用協調時間,也就是零時區。 -
time
包默認(為顯示提供時區)使用Local
時區。 - 平時使用的都是
Local
時間,數據庫存儲的時候要注意,一般orm
框架會自動實現這個。
func setTimeZone() { curLocalTime := time.Now() curUtcTime := curLocalTime.In(time.UTC) fmt.Println(curUtcTime) }
通常,我們使用?time.Local
?即可,偶爾可能會需要使用?UTC
。在解析時間時,心中一定記得有時區這么回事。當你發現時間出現莫名的情況時,很可能是因為時區的問題,特別是當時間相差 8 小時時。
時間格式化(時間類型轉字符串)
func time2TimeStr() { localTimeStr := time.Now().Format("2006-01-02 15:04:05") // UTC時間 utcTimeStr := time.Now().UTC().Format("2006-01-02 15:04:05") fmt.Println(localTimeStr, utcTimeStr) }
時間類型轉時間戳
func getCurTimeStamp() { // 時間戳,精確到秒 timestamp := time.Now().Unix() // 時間戳,精確到納秒 timestampNano := time.Now().UnixNano() fmt.Println(timestamp, timestampNano) }
相關函數或方法:
- time.Unix(sec, nsec int64) 通過 Unix 時間戳生成?
time.Time
?實例; - time.Time.Unix() 得到 Unix 時間戳;
- time.Time.UnixNano() 得到 Unix 時間戳的納秒表示;
時間戳轉時間類型
func timestamp2Time() { timestamp := time.Now().Unix() localTimeObj := time.Unix(timestamp, 0) fmt.Println(localTimeObj) }
時間字符串轉時間類型
func timeStr2Time() { timeStr := "2020-01-13 22:32:17" // 返回的是UTC時間 2020-01-13 22:32:17 +0000 UTC utcTimeObj, err := time.Parse("2006-01-02 15:04:05", timeStr) if err == nil { fmt.Println(utcTimeObj, utcTimeObj.Unix()) } // 返回的是當地時間 2020-01-13 22:32:17 +0800 CST localTimeObj, err := time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local) if err == nil { fmt.Println(localTimeObj) } }
time.Parse 解析出來的時區卻是 time.UTC(可以通過 Time.Location() 函數知道是哪個時區)。在中國,它們相差 8 小時。 所以,一般的,我們應該總是使用 time.ParseInLocation 來解析時間,并給第三個參數傳遞 time.Local。
時間計算
獲取時間類型具體內容
t := time.Now() fmt.Println("time.Now():", t) // 2020-10-24 22:10:53.328973 +0800 CST m=+0.006015101 year, month, day := t.Date() fmt.Println("日期:", year, month, day) // 2020 October 24 fmt.Println("一年中的第幾天:", t.YearDay()) // 298 fmt.Println("星期幾:", t.Weekday()) // Saturday fmt.Println("年:", t.Year()) // 2020 fmt.Println("月:", t.Month()) // October fmt.Println("日:", t.Day()) // 24 fmt.Println("時:", t.Hour()) // 22 fmt.Println("分:", t.Minute()) // 10 fmt.Println("秒:", t.Second()) // 53 fmt.Println("納秒:", t.Nanosecond()) // 328973000 fmt.Println("秒時間戳:", t.Unix()) // 1603548653 fmt.Println("納秒時間戳:", t.UnixNano()) // 1603548653328973000 fmt.Println("毫秒時間戳:", t.UnixNano() / 1e6) // 1603548653328
時間加減
轉換為Time類型比較容易做加減。
- 時間點可以使用 Before、After 和 Equal 方法進行比較。
- Sub 方法讓兩個時間點相減,生成一個 Duration 類型值(代表時間段)。
- Add 方法給一個時間點加上一個時間段,生成一個新的 Time 類型時間點。
func addTime() { curTime := time.Now() // 加1秒 addSecondTime := curTime.Add(time.Second * 1) // 加1分鐘 addMinuteTime := curTime.Add(time.Minute * 1) addMinuteTime2 := curTime.Add(time.Second * time.Duration(60*1)) fmt.Println(addSecondTime, addMinuteTime, addMinuteTime2) }
時間類型中有提前定義固定的時間長度常量,比如一小時的長度就是time.Hour
t := time.Now() addOneHour := t.Add(time.Hour) addTwoHour := t.Add(2 * time.Hour) fmt.Println("增加1小時:", addOneHour) fmt.Println("增加2小時:", addTwoHour) subTwoHour := t.Add(-2 * time.Hour) fmt.Println("減去2小時:", subTwoHour) addDate := t.AddDate(1, 0, 0) fmt.Println("增加1年:", addDate) // 2021-10-24 22:10:53.328973 +0800 CST subDate := t.AddDate(-1, 0, 0) fmt.Println("減去1年:", subDate) // 2019-10-24 22:10:53.328973 +0800 CST before := t.Before(t.Add(time.Hour)) fmt.Println("before:", before) after := t.After(t.Add(time.Hour)) fmt.Println("after:", after)
時間間隔(耗時)
t := time.Now() time.Sleep(2e9) // 休眠2秒 delta := time.Now().Sub(t) fmt.Println("時間差:", delta) // 2.0534341s
時間取整(向上取整向下取整)
t, _ := time.ParseInLocation("2006-01-02 15:04:05", "2016-06-13 15:34:39", time.Local) // 整點(向下取整) fmt.Println(t.Truncate(1 * time.Hour)) // 整點(最接近) fmt.Println(t.Round(1 * time.Hour)) // 整分(向下取整) fmt.Println(t.Truncate(1 * time.Minute)) // 整分(最接近) fmt.Println(t.Round(1 * time.Minute)) t2, _ := time.ParseInLocation("2006-01-02 15:04:05", t.Format("2006-01-02 15:00:00"), time.Local) fmt.Println(t2)
拓展
json時間轉換
前后端建議使用時間戳傳輸,不要使用時間字符串可以大大省心,如果非要使用字符串傳輸,在傳遞json的時候就需要反復的做解析相當的不友善,但也不是不能做。 方式一、省心方式,重定義時間類型
type Time time.Time const ( timeFormart = "2006-01-02 15:04:05" ) func (t *Time) UnmarshalJSON(data []byte) (err error) { now, err := time.ParseInLocation(`"`+timeFormart+`"`, string(data), time.Local) *t = Time(now) return } func (t Time) MarshalJSON() ([]byte, error) { b := make([]byte, 0, len(timeFormart)+2) b = append(b, '"') b = time.Time(t).AppendFormat(b, timeFormart) b = append(b, '"') return b, nil } func (t Time) String() string { return time.Time(t).Format(timeFormart) } type Person struct { Id int64 `json:"id"` Name string `json:"name"` Birthday Time `json:"birthday"` }
- 這種時間重定義了時間類型
time.Time
為Time
類型,所以在結構體使用的時候要注意不要用錯,結構體直接調用json
的解析反解析方法就可以,傳入字符串類型,解析為時間類型。
方式二、重寫結構體方法
type Person struct { Id int64 `json:"id"` Name string `json:"name"` Birthday Time `json:"_"` } func (p *People) UnmarshalJSON(b []byte) error { // 定義臨時類型 用來接受非`json:"_"`的字段 type tmp People // 用中間變量接收json串,tmp以外的字段用來接受`json:"_"`屬性字段 var s = &struct { tmp // string 先接收字符串類型,一會再轉換 Birthday string `json:"birthday"` }{} // 解析 err := json.Unmarshal(b, &s) if err != nil { return err } localTimeObj, err := time.ParseInLocation("2006-01-02 15:04:05", s.Birthday, time.Local) if err == nil { return err } s.tmp.Birthday = localTimeObj // tmp類型轉換回People,并賦值 *p = People(s.tmp) return nil }
作業
- 嘗試寫出時間戳轉字符串的代碼
- 嘗試求上個月最后一天
原文鏈接:https://juejin.cn/post/7187026108062105657
相關推薦
- 2022-05-01 oracle刪除超過N天數據腳本的方法_oracle
- 2022-05-22 Python?IO文件管理的具體使用_python
- 2022-04-06 Qt實現導出QTableWidget/QTableView數據_C 語言
- 2022-04-15 Android開發Jetpack組件Room用例講解_Android
- 2022-12-23 iOS?button響應流程圖文詳解_IOS
- 2022-11-05 Android?TabLayout?自定義樣式及使用詳解_Android
- 2022-06-20 使用Pyqt5制作屏幕錄制界面功能_python
- 2022-06-30 卷積神經網絡經典模型及其改進點學習匯總_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支