简介

在各个语言之中都有工夫类型的解决,因为这个地球是圆的(我好像在讲废话),有多个时区,每个时区的工夫不一样,在程序中有必要存在一种形式,或者说一种类型存储工夫,还能够通过一系列的办法转换成不同国家的工夫。

上问提到了工夫、时区,还有一个概念为两个工夫之间的差值,比方小熊每次能够保持1个小时(锤炼),1个小时这种工夫形容词就是工夫距离。

这就是三种工夫解决的类型。

类型

TimeLocationDuration 工夫、时区、工夫距离。它们都在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 (也就是时区)的两个实例:LocalUTC

  • Local 代表以后零碎本地时区;UTC 代表通用协调工夫,也就是零时区。
  • time 包默认(为显示提供时区)应用 Local 时区。
  • 平时应用的都是 Local 工夫,数据库存储的时候要留神,个别 orm 框架会主动实现这个。

默认就是Local中国工夫!

问题:时区这个怎么设置?传字符串进去吗?

curLocalTime := time.Now() //这是localcurUtcTime := 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解析和反解析有哪两个办法吗?有没有人重写过 UnmarshalJSONMarshalJSON。咱们来温习一下。

我写的书外面的提到在不同方法的接口,有可能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会尝试调用。看截图

  1. 先解析到匿名构造体变量中,birthday字段赋值给了s.Brithday,其余字段给了s.tmp
  2. s.Birthday是一个字符串类型,再把这个类型转换成工夫类型。
  3. localtime 放到 tmp 外面,tmp 就是之前的 people

所以返回的就是tmp, 才是咱们要的。

*p = People(s.tmp)

最初再创立一个People,把tmp传递过来。

【思考题】为什么这里还要创立一个,间接赋值s.tmp*p能够不?(这里我给你们挖了一个坑)。

我定义的是新类型,并不是创立,实际上是一个强制类型转换。哈哈哈,我就是蔫坏。

对于工夫解决的各种函数我也列在上面了,大家珍藏看就行了。还是刚刚提到的各种残缺代码。喜爱这篇文章的话点个在看,么么哒。

工夫操作

获取以后工夫

import timefunc getCurTime() {    // 本地工夫(如果是在中国,获取的是东八区工夫)    curLocalTime := time.Now()    // UTC工夫    curUTCTime := time.Now().UTC()    fmt.Println(curLocalTime, curUTCTime)}

时区设置

不同国家(有时甚至是同一个国家内的不同地区)应用不同的时区。对于要输出和输入工夫的程序来说,必须对系统所处的时区加以思考。Go 语言应用 Location 来示意地区相干的时区,一个 Location 可能示意多个时区。开展解说
time 包提供了 Location 的两个实例:LocalUTC

  • 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.006015101year, month, day := t.Date()fmt.Println("日期:", year, month, day) // 2020 October 24fmt.Println("一年中的第几天:", t.YearDay()) // 298fmt.Println("星期几:", t.Weekday()) // Saturdayfmt.Println("年:", t.Year()) // 2020fmt.Println("月:", t.Month()) // Octoberfmt.Println("日:", t.Day()) // 24fmt.Println("时:", t.Hour()) // 22fmt.Println("分:", t.Minute()) // 10fmt.Println("秒:", t.Second()) // 53fmt.Println("纳秒:", t.Nanosecond()) // 328973000fmt.Println("秒工夫戳:", t.Unix()) // 1603548653fmt.Println("纳秒工夫戳:", t.UnixNano()) // 1603548653328973000fmt.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 CSTsubDate := t.AddDate(-1, 0, 0)fmt.Println("减去1年:", subDate) // 2019-10-24 22:10:53.328973 +0800 CSTbefore := 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.Timeconst (    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类型,所以在构造体应用的时候要留神不要用错,构造体间接调用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}

作业

  1. 尝试写出工夫戳转字符串的代码
  2. 尝试求上个月最初一天

喜爱点个赞吧,求求了!

本文由mdnice多平台公布