欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Golang時間處理的踩坑及解決

 更新時間:2023年01月11日 09:08:39   作者:機智的程序員小熊  
在各個語言之中都有時間類型的處理,這篇文章主要和大家分享一下Golang進行時間處理時哪里最容易踩坑以及解決方法,需要的可以參考一下

簡介

在各個語言之中都有時間類型的處理,因為這個地球是圓的(我仿佛在講廢話),有多個時區(qū),每個時區(qū)的時間不一樣,在程序中有必要存在一種方式,或者說一種類型存儲時間,還可以通過一系列的方法轉(zhuǎn)換成不同國家的時間。

上問提到了時間、時區(qū),還有一個概念為兩個時間之間的差值,比如小熊每次可以堅持1個小時(鍛煉),1個小時這種時間形容詞就是時間間隔。

這就是三種時間處理的類型。

類型

Time、LocationDuration 時間、時區(qū)、時間間隔。它們都在time包里面。

Time時間類型

程序中應(yīng)使用 Time 類型值來保存和傳遞時間,一個結(jié)構(gòu)體,精確到納秒。里面的變量都是私有的用不到,先不去管他。

type Time struct {
    sec int64 //秒
    nsec int32 //納秒
    loc *Location //時區(qū)
}
  • 一個Time類型值可以被多個go程同時使用。因為它是 time.Time 類型,而不是 指針*time.Time 類型。
  • 時間需要初始化:IsZero 方法提供了檢驗時間是否是顯式初始化。
  • 時區(qū)類型作為Time結(jié)構(gòu)體中的一個字段,標(biāo)記這個時間當(dāng)前是哪個時區(qū)。

Duration  時間間隔,兩個時間之間的差值,以納秒為單位,最長 290 年,作為常識即可。

type Duration int64

時區(qū)

我們在使用time.Time類型一般都是Local時間,也就是本地時間,現(xiàn)在就是中國時間。

// 本地時間(如果是在中國,獲取的是東八區(qū)時間)
	curLocalTime := time.Now()
	// UTC時間
	curUTCTime := time.Now().UTC()

time 包提供了 Location (也就是時區(qū))的兩個實例:LocalUTC。

  • Local 代表當(dāng)前系統(tǒng)本地時區(qū);UTC 代表通用協(xié)調(diào)時間,也就是零時區(qū)。
  • time 包默認(rèn)(為顯示提供時區(qū))使用 Local 時區(qū)。
  • 平時使用的都是 Local 時間,數(shù)據(jù)庫存儲的時候要注意,一般 orm 框架會自動實現(xiàn)這個。

默認(rèn)就是Local中國時間!

問題:時區(qū)這個怎么設(shè)置?傳字符串進去嗎?

curLocalTime := time.Now() //這是local
curUtcTime := curLocalTime.In(time.UTC) //這是UTC

時區(qū)特別容易出錯,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。這個經(jīng)常有人出錯。解析字符串時,都以協(xié)調(diào)時UTC時間為準(zhǔn)。

還有另一個辦法,比較穩(wěn)。我們應(yīng)該總是使用 time.ParseInLocation 來解析時間,并給第三個參數(shù)傳遞 time.Local

localTimeObj, err := time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local)
    if err == nil {
        fmt.Println(localTimeObj)
    }

它返回的是time 類型是嗎?沒錯!這兩個返回的都是time類型。

問:這個會用在哪個場景?

好問題,問到點子上了!

時間解析的使用場景

前后端傳輸json數(shù)據(jù)的時候,或者數(shù)據(jù)庫存儲讀取的時候。前后端建議使用時間戳傳輸,不要使用時間字符串可以大大省心。數(shù)據(jù)庫如果使用orm的框架,一般是會自動處理時間存儲。

我們約定好用時間戳傳遞,總是有一些比較軸的同事一定要用字符串傳輸,你有沒有這樣的同事?如果非要使用字符串傳輸,在傳遞json的時候就需要反復(fù)的做解析相當(dāng)?shù)牟挥焉啤?/p>

但也不是不能做~~

大家了解過json解析和反解析有哪兩個方法嗎?有沒有人重寫過 UnmarshalJSONMarshalJSON。我們來復(fù)習(xí)一下。

我寫的書里面的提到在不同辦法的接口,有可能json字段的類型會發(fā)生改變,一般做兼容性處理的時候會重寫到。

看這個截圖,字符串轉(zhuǎn)換成結(jié)構(gòu)體,反過來結(jié)構(gòu)體轉(zhuǎn)換成字符串,就是用MarshalJSON。

type Person struct {
    Id       int64  `json:"id"`
    Name     string `json:"name"`
    Birthday Time   `json:"_"`
}

比如一個結(jié)構(gòu)體,里面有一個時間類型,你的前端同事又不傳時間戳,你就得手動轉(zhuǎn)換成時間類型,或者時間戳,這個你自己決定。這里是 Birthday 舉例,我的注解里面用的json是一個下劃線,在解析的時候就不會寫入。

問:這個不寫入, 是 json庫實現(xiàn)的,還是自己實現(xiàn)的?

json庫。json庫讀取注解,匹配json中的字段名稱,寫入到結(jié)構(gòu)體中。我的注解里寫成了下劃線,這只是一個占位符,習(xí)慣上這么寫。你也可以寫成-中杠線。

我先寫了一個People的反解析函數(shù),json.UnmarshalJSON會嘗試調(diào)用??唇貓D

  • 先解析到匿名結(jié)構(gòu)體變量中,birthday字段賦值給了s.Brithday,其他字段給了s.tmp
  • s.Birthday是一個字符串類型,再把這個類型轉(zhuǎn)換成時間類型。
  • localtime 放到 tmp 里面,tmp 就是之前的 people。

所以返回的就是tmp, 才是我們要的。

*p = People(s.tmp)

最后再創(chuàng)建一個People,把tmp傳遞過去。

【思考題】為什么這里還要創(chuàng)建一個,直接賦值s.tmp*p可以不?(這里我給你們挖了一個坑)。

我定義的是新類型,并不是創(chuàng)建,實際上是一個強制類型轉(zhuǎn)換。哈哈哈,我就是蔫壞。

關(guān)于時間處理的各種函數(shù)我也列在下面了,大家收藏看就行了。還是剛剛提到的各種完整代碼。

時間操作

獲取當(dāng)前時間

import time

func getCurTime() {
	// 本地時間(如果是在中國,獲取的是東八區(qū)時間)
	curLocalTime := time.Now()
	// UTC時間
	curUTCTime := time.Now().UTC()
	fmt.Println(curLocalTime, curUTCTime)
}

時區(qū)設(shè)置

不同國家(有時甚至是同一個國家內(nèi)的不同地區(qū))使用不同的時區(qū)。對于要輸入和輸出時間的程序來說,必須對系統(tǒng)所處的時區(qū)加以考慮。Go 語言使用 Location 來表示地區(qū)相關(guān)的時區(qū),一個 Location 可能表示多個時區(qū)。展開講解 time 包提供了 Location 的兩個實例:LocalUTC

  • Local 代表當(dāng)前系統(tǒng)本地時區(qū);UTC 代表通用協(xié)調(diào)時間,也就是零時區(qū)。
  • time 包默認(rèn)(為顯示提供時區(qū))使用 Local 時區(qū)。
  • 平時使用的都是Local 時間,數(shù)據(jù)庫存儲的時候要注意,一般orm 框架會自動實現(xiàn)這個。
func setTimeZone() {
	curLocalTime := time.Now()
	curUtcTime := curLocalTime.In(time.UTC)
	fmt.Println(curUtcTime)
}

通常,我們使用 time.Local 即可,偶爾可能會需要使用 UTC。在解析時間時,心中一定記得有時區(qū)這么回事。當(dāng)你發(fā)現(xiàn)時間出現(xiàn)莫名的情況時,很可能是因為時區(qū)的問題,特別是當(dāng)時間相差 8 小時時。

時間格式化(時間類型轉(zhuǎn)字符串)

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)
}

時間類型轉(zhuǎn)時間戳

func getCurTimeStamp() {
	// 時間戳,精確到秒
	timestamp := time.Now().Unix()
	// 時間戳,精確到納秒
	timestampNano := time.Now().UnixNano()
	fmt.Println(timestamp, timestampNano)
}

相關(guān)函數(shù)或方法:

  • time.Unix(sec, nsec int64) 通過 Unix 時間戳生成 time.Time 實例;
  • time.Time.Unix() 得到 Unix 時間戳;
  • time.Time.UnixNano() 得到 Unix 時間戳的納秒表示;

時間戳轉(zhuǎn)時間類型

func timestamp2Time() {
	timestamp := time.Now().Unix()
	localTimeObj := time.Unix(timestamp, 0)
	fmt.Println(localTimeObj)
}

時間字符串轉(zhuǎn)時間類型

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())
	}

	// 返回的是當(dāng)?shù)貢r間 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 解析出來的時區(qū)卻是 time.UTC(可以通過 Time.Location() 函數(shù)知道是哪個時區(qū))。在中國,它們相差 8 小時。 所以,一般的,我們應(yīng)該總是使用 time.ParseInLocation 來解析時間,并給第三個參數(shù)傳遞 time.Local。

時間計算

獲取時間類型具體內(nèi)容

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

時間加減

轉(zhuǎn)換為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時間轉(zhuǎn)換

前后端建議使用時間戳傳輸,不要使用時間字符串可以大大省心,如果非要使用字符串傳輸,在傳遞json的時候就需要反復(fù)的做解析相當(dāng)?shù)牟挥焉?,但也不是不能做?方式一、省心方式,重定義時間類型

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.TimeTime類型,所以在結(jié)構(gòu)體使用的時候要注意不要用錯,結(jié)構(gòu)體直接調(diào)用json的解析反解析方法就可以,傳入字符串類型,解析為時間類型。

方式二、重寫結(jié)構(gòu)體方法

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 先接收字符串類型,一會再轉(zhuǎn)換
		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類型轉(zhuǎn)換回People,并賦值
	*p = People(s.tmp)
	return nil
}

到此這篇關(guān)于詳解Golang時間處理的踩坑及解決的文章就介紹到這了,更多相關(guān)Golang時間處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang開發(fā)庫的集合及作用說明

    Golang開發(fā)庫的集合及作用說明

    這篇文章主要為大家介紹了Golang開發(fā)golang庫的集合及簡單的作用說明,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2021-11-11
  • Golang極簡入門教程(三):并發(fā)支持

    Golang極簡入門教程(三):并發(fā)支持

    這篇文章主要介紹了Golang極簡入門教程(三):并發(fā)支持,本文講解了goroutine線程、channel 操作符等內(nèi)容,需要的朋友可以參考下
    2014-10-10
  • go?mod?tidy報錯解決方法詳解

    go?mod?tidy報錯解決方法詳解

    這篇文章主要為大家介紹了go?mod?tidy報錯解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • Go語言中sync.Mutex的使用方法

    Go語言中sync.Mutex的使用方法

    本文主要介紹了golang中sync.Mutex的實現(xiàn)方法,mutex主要有兩個 method:Lock()和Unlock(),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • Golang實現(xiàn)http重定向https

    Golang實現(xiàn)http重定向https

    這篇文章介紹了Golang實現(xiàn)http重定向https的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07
  • Golang defer延遲語句的實現(xiàn)

    Golang defer延遲語句的實現(xiàn)

    defer擁有注冊延遲調(diào)用的機制,本文主要介紹了Golang defer延遲語句的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07
  • ???????Golang實現(xiàn)RabbitMQ中死信隊列幾種情況

    ???????Golang實現(xiàn)RabbitMQ中死信隊列幾種情況

    本文主要介紹了???????Golang實現(xiàn)RabbitMQ中死信隊列幾種情況,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • golang struct, map, json之間的相互轉(zhuǎn)換

    golang struct, map, json之間的相互轉(zhuǎn)換

    本文用于記錄我在 golang 學(xué)習(xí)階段遇到的類型轉(zhuǎn)換問題,針對的是 json 、map、struct 之間相互轉(zhuǎn)換的問題,感興趣的可以了解一下
    2021-06-06
  • Golang中如何實現(xiàn)枚舉詳析

    Golang中如何實現(xiàn)枚舉詳析

    舉就是將數(shù)據(jù)值一一列出來,枚舉可以用來表示一些固定的值,枚舉是常量組成的,下面這篇文章主要給大家介紹了關(guān)于Golang中如何實現(xiàn)枚舉的相關(guān)資料,需要的朋友可以參考下
    2022-07-07
  • vscode如何debug調(diào)試golang代碼

    vscode如何debug調(diào)試golang代碼

    古話說工欲善其事必先利其器,Go語言程序的開發(fā)者而言,當(dāng)下最火的IDE應(yīng)該非微軟的Visual Studio Code莫屬,本文主要介紹了vscode如何debug調(diào)試golang代碼,感興趣的可以了解一下
    2024-03-03

最新評論