<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Golang時間處理容易踩坑,小心損失百萬

          共 9304字,需瀏覽 19分鐘

           ·

          2023-01-11 14:50

          bf6afa48b2bedbeba3fcc5069bed46f0.webp

          簡介

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

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

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

          類型

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

          Time時間類型

          程序中應使用 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)體中的一個字段,標記這個時間當前是哪個時區(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 代表當前系統(tǒng)本地時區(qū);UTC 代表通用協(xié)調(diào)時間,也就是零時區(qū)。
          • time 包默認(為顯示提供時區(qū))使用 Local 時區(qū)。
          • 平時使用的都是 Local 時間,數(shù)據(jù)庫存儲的時候要注意,一般 orm 框架會自動實現(xiàn)這個。

          默認就是Local中國時間!

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

                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時間為準。

          還有另一個辦法,比較穩(wěn)。我們應該總是使用 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類型。

          977329143d78795ef68b8f91816c6198.webp

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

          好問題,問到點子上了!

          時間解析的使用場景

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

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

          但也不是不能做~~

          大家了解過json解析和反解析有哪兩個方法嗎?有沒有人重寫過 UnmarshalJSON 和 ?MarshalJSON。我們來復習一下。

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

          51be6566817c08c9a4eadef9438e3c6b.webp

          看這個截圖,字符串轉(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)體中。我的注解里寫成了下劃線,這只是一個占位符,習慣上這么寫。你也可以寫成-中杠線。

          635509252b32d6d9db59560d1989d944.webp

          我先寫了一個People的反解析函數(shù),json.UnmarshalJSON會嘗試調(diào)用。看截圖

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

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

                *p?=?People(s.tmp)

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

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

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

          關于時間處理的各種函數(shù)我也列在下面了,大家收藏看就行了。還是剛剛提到的各種完整代碼。喜歡這篇文章的話點個在看,么么噠。

          時間操作

          獲取當前時間

                import?time

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

          時區(qū)設置

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

          • Local 代表當前系統(tǒng)本地時區(qū);UTC 代表通用協(xié)調(diào)時間,也就是零時區(qū)。
          • time 包默認(為顯示提供時區(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ū)這么回事。當你發(fā)現(xiàn)時間出現(xiàn)莫名的情況時,很可能是因為時區(qū)的問題,特別是當時間相差 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)
          }

          相關函數(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())
          ?}

          ?//?返回的是當?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 小時。所以,一般的,我們應該總是使用 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的時候就需要反復的做解析相當?shù)牟挥焉疲膊皇遣荒茏觥7绞揭弧⑹⌒姆绞剑囟x時間類型

                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
          }

          作業(yè)

          1. 嘗試寫出時間戳轉(zhuǎn)字符串的代碼
          2. 嘗試求上個月最后一天
          轉(zhuǎn)發(fā)下噻,關注下噻,在看下噻,實在不行寫個作業(yè)?
          瀏覽 72
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  av色先锋 | 2021最新无码视频 | 夜夜嗨AⅤ一区二区三区 | 操逼网站视频 | 波多野结衣网站 |