• Go 语言标准库之 time 包


    Go 语言的 time 包提供了时间的显示和测量用的函数。日历的计算采用的是公历。

    time.Location 类型

    Go 语言使用time.Location来表示地区相关的时区,一个time.Location可能表示多个时区。

    // time.Location 代表一个(关联到某个时间点的)地点,以及该地点所在的时区
    type Location struct {
        // 内含隐藏或非导出字段
    }
    
    // Local 代表系统本地,对应本地时区
    var Local *Location = &localLoc
    
    // UTC 代表通用协调时间,对应零时区。time 包默认使用零时区
    var UTC *Location = &utcLoc
    
    // 返回使用给定的名字创建的 Location
    // 如果 name 是 "" 或 "UTC",返回 UTC;如果 name 是 "Local",返回 Local;
    // 否则 name 应该是 IANA 时区数据库里有记录的地点名,如 "America/New_York"
    func LoadLocation(name string) (*Location, error)
    
    // 自定义地点和时区名称 name,在零时区基础上根据时间偏移量 offset(单位秒)创建并返回一个 Location
    func FixedZone(name string, offset int) *Location
    
    // 返回对时区信息的描述,返回值绑定为 LoadLocation 或 FixedZone 函数创建 l 时的 name 参数
    func (l *Location) String() string
    

    ☕️ 示例代码

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        // 获取本地当前时间
        now := time.Now()
    
        // 获取本地时区
        // 方式一
        loc1 := time.Local
        fmt.Println(loc1.String(), now.In(loc1)) // Local 2021-12-04 14:19:42.8397569 +0800 CST
        // 方式二
        loc2, _ := time.LoadLocation("Local")
        fmt.Println(loc2.String(), now.In(loc2)) // Local 2021-12-04 14:19:42.8397569 +0800 CST
        // 方式三
        loc3 := now.Location()
        fmt.Println(loc3.String(), now.In(loc3)) // Local 2021-12-04 14:19:42.8397569 +0800 CST
    
        // 获取零时区
        // 方式一
        utc1 := time.UTC
        fmt.Println(utc1.String(), now.In(utc1)) // UTC 2021-12-04 06:19:42.8397569 +0000 UTC
        // 方式二
        utc2, _ := time.LoadLocation("")
        fmt.Println(utc2.String(), now.In(utc2)) // UTC 2021-12-04 06:19:42.8397569 +0000 UTC
        // 方式三
        utc3, _ := time.LoadLocation("UTC")
        fmt.Println(utc3.String(), now.In(utc3)) // UTC 2021-12-04 06:19:42.8397569 +0000 UTC
    
        // 指定地点和时区,东八区
        shanghai, _ := time.LoadLocation("Asia/Shanghai")
        fmt.Println(shanghai.String(), now.In(shanghai)) // Asia/Shanghai 2021-12-04 14:19:42.8397569 +0800 CST
    
        // 自定义地点和时区名称,东八区
        beijing := time.FixedZone("Beijing", 8*60*60)
        fmt.Println(beijing.String(), now.In(beijing)) // Beijing 2021-12-04 14:19:42.8397569 +0800 Beijing
    }
    

    time.Time 类型

    time.Time类型表示时间,我们可以通过以下函数获取时间对象,然后获取时间对象的年月日时分秒等信息。

    获取 Time 对象

    // time.Time 代表一个纳秒精度的时间点
    type Time struct {
        // 内含隐藏或非导出字段
    }
    
    // 返回本地当前时间
    func Now() Time
    
    // 返回一个时区为 loc、当地时间为 year-month-day hour:min:sec + nsec nanoseconds 的时间
    func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
    
    // 创建一个本地时间,对应 sec 和 nsec 表示的 Unix 时间(从 January 1, 1970 UTC 至该时间的秒数和纳秒数)
    func Unix(sec int64, nsec int64) Time
    
    // 返回采用本地地点和本地时区,但指向同一时间点的 Time
    func (t Time) Local() Time
    
    // 返回采用 UTC 和零时区,但指向同一时间点的 Time
    func (t Time) UTC() Time
    
    // 返回采用 loc 指定的地点和时区,但指向同一时间点的 Time
    func (t Time) In(loc *Location) Time
    

    ⭐️ 示例代码

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        // Now:获取本地当前时间
        now := time.Now()
        fmt.Println(now) // 2021-12-04 14:43:13.5010095 +0800 CST m=+0.004735101
    
        // Date:自定义时间
        setTime1 := time.Date(2021, 12, 4, 12, 3, 56, 0, now.Location())
        fmt.Println(setTime1) // 2021-12-04 12:03:56 +0800 CST
    
        // Unix:自定义时间
        setTime2 := time.Unix(setTime1.Unix(), 1000).In(now.Location())
        fmt.Println(setTime2) // 2021-12-04 12:03:56.000001 +0800 CST
    }
    

    获取 Time 基本信息

    // Weekday 代表一周的某一天
    type Weekday int
    
    const (
        Sunday Weekday = iota
        Monday
        Tuesday
        Wednesday
        Thursday
        Friday
        Saturday
    )
    
    // String 返回该日(周几)的英文名("Sunday"、"Monday",……)
    func (d Weekday) String() string
    
    // Month代表一年的某个月
    type Month int
    
    const (
        January Month = 1 + iota
        February
        March
        April
        May
        June
        July
        August
        September
        October
        November
        December
    )
    
    // String 返回月份的英文名("January","February",……)
    func (m Month) String() string 
    
    // 返回 t 的地点和时区信息
    func (t Time) Location() *Location
    
    // 计算 t 所在的时区,返回该时区的规范名(如"CET")和该时区相对于 UTC 的时间偏移量(单位秒)
    func (t Time) Zone() (name string, offset int)
    
    // 将 t 表示为 Unix 时间,即从时间点 January 1, 1970 UTC 到时间点 t 所经过的时间(单位秒)
    func (t Time) Unix() int64
    
    // 将 t 表示为 Unix 时间,即从时间点 January 1, 1970 UTC 到时间点 t 所经过的时间(单位纳秒)
    func (t Time) UnixNano() int64
    
    // 返回时间点 t 对应的年、月、日
    func (t Time) Date() (year int, month Month, day int)
    
    // 返回 t 对应的那一天的时、分、秒
    func (t Time) Clock() (hour, min, sec int)
    
    // 返回时间点 t 对应的年份
    func (t Time) Year() int
    
    // 返回时间点 t 对应那一年的第几月
    func (t Time) Month() Month
    
    // 返回时间点 t 对应的 ISO 9601 标准下的年份和星期编号,星期编号范围 [1,53]
    // 1 月 1 号到 1 月 3 号可能属于上一年的最后一周,12 月 29 号到 12 月 31 号可能属于下一年的第一周
    func (t Time) ISOWeek() (year, week int)
    
    // 返回时间点 t 对应的那一年的第几天,平年的返回值范围 [1,365],闰年 [1,366]
    func (t Time) YearDay() int
    
    // 返回时间点 t 对应那一月的第几日
    func (t Time) Day() int
    
    // 返回时间点 t 对应的那一周的周几
    func (t Time) Weekday() Weekday
    
    // 返回 t 对应的那一天的第几小时,范围 [0, 23]
    func (t Time) Hour() int
    
    // 返回 t 对应的那一小时的第几分种,范围 [0, 59]
    func (t Time) Minute() int
    
    // 返回 t 对应的那一分钟的第几秒,范围 [0, 59]
    func (t Time) Second() int
    
    // 返回 t 对应的那一秒内的纳秒偏移量,范围 [0, 999999999]
    func (t Time) Nanosecond() int
    

    ✏️ 示例代码

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        now := time.Now()
    
        // 地点和时区信息
        fmt.Println(now.Location()) // Local
        // 时区
        fmt.Println(now.Zone()) // CST 28800
        // Unix 时间(单位秒)
        fmt.Println(now.Unix()) // 1638602223
        // Unix 时间(单位纳秒)
        fmt.Println(now.UnixNano())   // 1638602223393227200
        fmt.Println(now.Date())       // 2021 December 4
        fmt.Println(now.Clock())      // 15 17 3
        fmt.Println(now.Year())       // 2021
        fmt.Println(now.Month())      // December
        fmt.Println(now.ISOWeek())    // 2021 48
        fmt.Println(now.YearDay())    // 338
        fmt.Println(now.Day())        // 4
        fmt.Println(now.Weekday())    // Saturday
        fmt.Println(now.Hour())       // 15
        fmt.Println(now.Minute())     // 17
        fmt.Println(now.Second())     // 3
        fmt.Println(now.Nanosecond()) // 393227200
    }
    

    Time 比较和运算

    // Duration 类型代表两个时间点之间经过的时间,以纳秒为单位。可表示的最长时间段大约 290 年
    type Duration int64 
    
    const (
        Nanosecond  Duration = 1
        Microsecond          = 1000 * Nanosecond
        Millisecond          = 1000 * Microsecond
        Second               = 1000 * Millisecond
        Minute               = 60 * Second
        Hour                 = 60 * Minute
    )
    
    // ParseDuration 解析一个时间段字符串
    // 一个时间段字符串是一个序列,每个片段包含可选的正负号、十进制数、可选的小数部分和单位后缀,如"300ms"、"-1.5h"、"2h45m"。
    // 合法的单位有"ns"、"us"、"µs"、"ms"、"s"、"m"、"h"
    func ParseDuration(s string) (Duration, error)
    
    // Hours 将时间段表示为 float64 类型的小时数
    func (d Duration) Hours() float64
    
    // Hours 将时间段表示为 float64 类型的分钟数
    func (d Duration) Minutes() float64
    
    // Hours 将时间段表示为 float64 类型的秒数
    func (d Duration) Seconds() float64
    
    // Hours 将时间段表示为 int64 类型的纳秒数,等价于 int64(d)
    func (d Duration) Nanoseconds() int64
    
    // 返回时间段采用 "72h3m0.5s" 格式的字符串表示
    func (d Duration) String() string
    
    // 表示按照 m 给定的单位,返回四舍五入
    func (d Duration) Round(m Duration) Duration
    
    // 表示按照 m 给定的单位,返回舍尾数计算
    func (d Duration) Truncate(m Duration) Duration
    
    // 报告 t 是否代表 Time 零值的时间点,January 1, year 1, 00:00:00 UTC
    func (t Time) IsZero() bool
    
    // 判断两个时间是否相同,会考虑时区的影响,因此不同时区标准的时间也可以正确比较
    // 本方法和用 t == u 不同,这种方法还会比较地点和时区信息
    func (t Time) Equal(u Time) bool
    
    // 如果 t 代表的时间点在 u 之前,返回真;否则返回假
    func (t Time) Before(u Time) bool
    
    // 如果 t 代表的时间点在 u 之后,返回真;否则返回假
    func (t Time) After(u Time) bool
    
    // 返回时间点 t + d
    func (t Time) Add(d Duration) Time
    
    // 返回增加了给出的年份、月份和天数的时间点 Time。例如,时间点 January 1, 2011 调用 AddDate(-1, 2, 3) 会返回 March 4, 2010
    // AddDate 会将结果规范化,类似 Date 函数的做法。因此,举个例子,给时间点 October 31 添加一个月,会生成时间点 December 1
    func (t Time) AddDate(years int, months int, days int) Time
    
    // 返回一个时间段 t - u。如果结果超出了 Duration 可以表示的最大值/最小值,将返回最大值/最小值
    // 要获取时间点 t-d(d 为 Duration),可以使用 t.Add(-d)
    func (t Time) Sub(u Time) Duration
    
    // 返回从 t 到现在经过的时间,等价于 time.Now().Sub(t)
    func Since(t Time) Duration
    
    // 返回从现在到 t 经过的时间,等价于 t.Sub(time.Now())
    func Until(t Time) Duration
    

    示例代码

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        now := time.Now()
        time1 := time.Date(2021, 12, 4, 12, 3, 56, 0, now.Location())
    
        fmt.Println(now.IsZero())            // false
        fmt.Println(now.Equal(time1))        // false
        fmt.Println(now.Before(time1))       // false
        fmt.Println(now.After(time1))        // true
        fmt.Println(now.Add(time.Hour))      // 2021-12-04 17:16:41.2619698 +0800 CST m=+3600.006734601
        fmt.Println(now.Add(-time.Hour))     // 2021-12-04 15:16:41.2619698 +0800 CST m=-3599.993265399
        fmt.Println(now.AddDate(1, 1, 1))    // 2023-01-05 16:16:41.2619698 +0800 CST
        fmt.Println(now.AddDate(-1, -1, -1)) // 2020-11-03 16:16:41.2619698 +0800 CST
        fmt.Println(now.Sub(time1))          // 4h12m45.2619698s
        fmt.Println(time.Since(time1))       // 4h12m45.2843379s
        fmt.Println(time.Until(time1))       // -4h12m45.2843379s
    }
    

    Time 与 string 转换

    // 解析并返回一个格式化的时间字符串所代表的时间,缺少时区信息时,Parse 会将时区设置为 UTC 时间
    // layout 定义了参考时间:Mon Jan 2 15:04:05 -0700 MST 2006
    func Parse(layout, value string) (Time, error)
    
    // ParseInLocation 类似 Parse 但有两个重要的不同之处
    // 第一,当缺少时区信息时,Parse 将时区设置为 UTC 时间,而 ParseInLocation 将时区设置为 loc
    // 第二,当时间字符串 value 提供了时区偏移量信息时,Parse 会尝试去匹配本地时区,而 ParseInLocation 会去匹配 loc
    func ParseInLocation(layout, value string, loc *Location) (Time, error)
    
    // 将时间字符串转换为 time.Duration 对象
    // 字符串可以使用这些单位: "ns", "us" (or "µs"), "ms", "s", "m", "h"
    func ParseDuration(s string) (Duration, error)
    
    // 根据 layout 指定的格式返回 t 代表的时间点的格式化文本表示
    // layout 定义了参考时间:Mon Jan 2 15:04:05 -0700 MST 2006
    func (t Time) Format(layout string) string
    
    // 该方法为基本格式化时间对象为字符串,用该方法可以避免申请临时对象
    // 需要由用户控制传入 buf 的长度,如果长度不够,则返回值是扩容后的可用的数据,而 buf 里面数据无效
    func (t Time) AppendFormat(b []byte, layout string) []byte
    
    // 返回采用如下格式字符串的格式化时间:"2006-01-02 15:04:05.999999999 -0700 MST"
    func (t Time) String() string
    

    ✌ 示例代码

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        // Go 语言格式化时间模板不是常见的 Y-m-d H:M:S,而是一个固定时间,记忆口诀为 2006 1 2 3 4 5
        const timeFormat = "2006-01-02 15:04:05"
        now := time.Now()
    
        // 将字符串转换换为时间
        fmt.Println(time.Parse(timeFormat, "2021-12-04 10:53:58"))                           // 2021-12-04 10:53:58 +0000 UTC <nil>
        fmt.Println(time.ParseInLocation(timeFormat, "2021-12-04 10:53:58", now.Location())) // 2021-12-04 10:53:58 +0800 CST <nil>
        fmt.Println(time.ParseDuration("24h5m6s123ms456us321ns"))                            // 24h5m6.123456321s <nil>
    
        // 将当前时间转换为字符串
        fmt.Println(now.Format(timeFormat)) // 2021-12-04 16:56:38
        
        buf := make([]byte, 0, 64)
        fmt.Printf("%s\n", now.AppendFormat(buf, timeFormat)) // 2021-12-04 16:56:38
    }
    

    time.Timer 类型

    // time.Timer 类型代表单次时间事件(到达指定时间触发且只触发一次)
    // 当 Timer 到期时,当时的时间会被发送给 C,除非 Timer 是被 AfterFunc 函数创建的。
    type Timer struct {
        C <-chan Time
        // 内含隐藏或非导出字段
    }
    
    // 创建一个 Timer,它会在最少过去时间段 d 后到期,向其自身的 C 字段发送当时的时间
    func NewTimer(d Duration) *Timer
    
    // 另起一个 Go 协程等待时间段 d 过去,然后调用 f
    // 它返回一个 Timer,可以通过调用其 Stop 方法来取消等待和对 f 的调用。
    func AfterFunc(d Duration, f func()) *Timer
    
    // 使 t 重新开始计时,(本方法返回后再)等待时间段 d 过去后到期
    // 如果调用时 t 还在等待中会返回真;如果 t 已经到期或者被停止了会返回假
    func (t *Timer) Reset(d Duration) bool
    
    // 停止 Timer 的执行。如果停止了 t 会返回真;如果 t 已经被停止或者过期了会返回假
    // Stop 不会关闭通道 t.C,以避免从该通道的读取不正确的成功
    func (t *Timer) Stop() bool
    
    // 会在另一线程经过时间段 d 后向返回值发送当时的时间。等价于 NewTimer(d).C
    func After(d Duration) <-chan Time
    
    // Sleep 阻塞当前 Go 协程至少 d 代表的时间段。d <= 0 时,Sleep 会立刻返回
    func Sleep(d Duration)
    

    ✍ 示例代码

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        // 大多数场景用下面这种方式处理超时逻辑
        // FIXME: 特别注意下面方法存在内存泄露,当大量调用chanTimeout
        // FIXME: 会产生大量time.After,此时如果都在超时时间内走handle
        // FIXME: 那么time.After产生的对象都占着内存,直到超过timeout才会GC释放
        chanTimeout := func(c <-chan int, timeout time.Duration) {
            select {
            case tmp, ok := <-c:
                // handle(tmp, ok)
                fmt.Println(tmp, ok)
            case <-time.After(timeout):
                fmt.Println("timeout")
            }
        }
        // FIXME: 使用下面方法更安全,当在超时时间内走到处理流程,手动释放内存
        chanTimeout = func(c <-chan int, timeout time.Duration) {
            t := time.NewTimer(timeout)
            select {
            case tmp, ok := <-c:
                t.Stop() // 当走正常逻辑时手动停掉timer
                // handle(t, ok)
                fmt.Println(tmp, ok)
            case <-t.C:
                fmt.Println("timeout")
            }
        }
    
        send := make(chan int)
        go chanTimeout(send, time.Second)
        time.Sleep(time.Millisecond * 800)
        select {
        case send <- 100: // 在timeout之前进入处理逻辑
        default:
        }
    
        go chanTimeout(send, time.Second)
        time.Sleep(time.Second * 2)
        select { // 可以尝试不用select + default,只简单的使用send <- 200会不会报错
        case send <- 200: // 直接进入timeout逻辑
        default:
        }
    
        fmt.Println(time.Now().String())
        timer := time.AfterFunc(time.Second, func() {
            fmt.Println(time.Now().String())
        })
        time.Sleep(time.Second * 2)
        timer.Reset(time.Second * 5) // 重置一下,5秒后会再打印一条
        time.Sleep(time.Second * 6)
        select {
        case <-timer.C:
        default:
        }
    }
    

    time.Ticker 类型

    // time.Ticker 保管一个通道,并每隔一段时间向其传递 "tick"(间隔特定时间触发)
    type Ticker struct {
        C <-chan Time // 周期性传递时间信息的通道
        // 内含隐藏或非导出字段
    }
    
    // 返回一个新的 Ticker,该 Ticker 包含一个通道字段,并会每隔时间段 d 就向该通道发送当时的时间
    // 它会调整时间间隔或者丢弃 tick 信息以适应反应慢的接收者。如果 d<=0 会 panic。关闭该 Ticker 可以释放相关资源
    func NewTicker(d Duration) *Ticker
    
    // Tick 是 NewTicker 的封装,只提供对 Ticker 的通道的访问。如果不需要关闭 Ticker,本函数就很方便
    func Tick(d Duration) <-chan Time
    
    // 关闭一个 Ticker,在关闭后,将不会发送更多的 tick 信息。Stop 不会关闭通道 t.C,以避免从该通道的读取不正确的成功
    func (t *Ticker) Stop()
    

    示例代码

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        i := 0
        // 按照如下方式定时获取 time 非常简洁
        for t := range time.Tick(time.Second) {
            i++
            if i > 3 {
                break
            }
            fmt.Println(t.String())
        }
    
        // 如下测试是常规用法
        t := time.NewTicker(time.Second)
        send := make(chan int)
        go func(c chan<- int) {
            i := 0
            for {
                time.Sleep(time.Millisecond * 600)
                c <- i
                i++
            }
        }(send)
        go func(c <-chan int) {
            for {
                select {
                case tmp, ok := <-t.C:
                    fmt.Println(tmp.String(), ok)
                case tmp, ok := <-c:
                    fmt.Println(tmp, ok)
                }
            }
        }(send)
        time.Sleep(time.Second * 10)
    
        t.Reset(time.Second)
        t.Stop()
    }
    

    参考

    1. 关于golang的time包总结
    2. Go 语言 time 包常用用法笔记
    3. golang time包的用法详解
  • 相关阅读:
    django 关于render的返回数据
    关于 eval 的报错 Uncaught ReferenceError: False is not defined
    Unexpected token o in JSON at position 1 at JSON.parse (<anonymous>) SyntaxError: Unexpected token R in JSON at position 0 at JSON.parse (<anonymous>)
    ajax 异步请求返回只刷新一次页面
    线程
    IO
    IO初步,字节输入流和字节输出流
    File、FileFilter、递归初步
    Map、可变参数、静态导入、Collections、Arrays、集合嵌套
    Collection单列集合中的常用实现类
  • 原文地址:https://www.cnblogs.com/zongmin/p/15649056.html
Copyright © 2020-2023  润新知