zl程序教程

您现在的位置是:首页 >  后端

当前栏目

go time包详解

Go 详解 Time
2023-09-11 14:15:46 时间


时间可分为时间点与时间段,golang 也不例外,提供了以下两种基础类型

  • 时间点(Time)
  • 时间段(Duration)

除此之外 golang 也提供了以下类型,做一些特定的业务

  • 时区(Location)
  • Ticker
  • Timer(定时器)

我们将按以上顺序来介绍 time 包的使用。

1 时间点(Time)

go 针对不同的参数类型提供了以下初始化的方式

package main
import "fmt"
import "time"
func main() {
	fmt.Println(time.Now())
    fmt.Println(time.Now().Local())
    fmt.Println(time.Now().Local().Hour())
}

输出结果

2020-11-23 11:10:41.7487379 +0800 CST m=+0.002993301
2020-11-23 11:10:41.7597131 +0800 CST
11
$ cat time1.go
package main
import "time"
import  "fmt"
func main() {

// func Now() Time
      fmt.Println(time.Now())

      // func Parse(layout, value string) (Time, error)
      fmt.Println(time.Parse("2016-01-02 15:04:05", "2018-04-23 12:24:51"))

      // func ParseInLocation(layout, value string, loc *Location) (Time, error) (layout已带时区时可直接用Parse)
      fmt.Println(time.ParseInLocation("2006-01-02 15:04:05", "2017-05-11 14:06:06", time.Local))

      // func Unix(sec int64, nsec int64) Time
      fmt.Println(time.Unix(1e9, 0))

      // func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
      fmt.Println(time.Date(2018, 1, 2, 15, 30, 10, 0, time.Local))

      // func (t Time) In(loc *Location) Time 当前时间对应指定时区的时间
      loc, _ := time.LoadLocation("America/Los_Angeles")
      fmt.Println(time.Now().In(loc))
}
[root@localhost time]# go run time1.go 
2020-02-27 17:36:07.963713722 +0800 CST m=+0.000218135
0001-01-01 00:00:00 +0000 UTC parsing time "2018-04-23 12:24:51": month out of range
2017-05-11 14:06:06 +0800 CST <nil>
2001-09-09 09:46:40 +0800 CST
2018-01-02 15:30:10 +0800 CST
2020-02-27 01:36:07.963939116 -0800 PST

2 格式化

to string

格式化为字符串我们需要使用 time.Format 方法来转换成我们想要的格式

 fmt.Println(time.Now().Format("2006-01-02 15:04:05"))  // 2018-04-24 10:11:20
 fmt.Println(time.Now().Format(time.UnixDate))      // Tue Apr 24 09:59:02 CST 2018

复制代码Format 函数中可以指定你想使用的格式,同时 time 包中也给了一些我们常用的格式

const (
	ANSIC       = "Mon Jan _2 15:04:05 2006"
	UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
	RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
	RFC822      = "02 Jan 06 15:04 MST"
	RFC822Z     = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
	RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
	RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
	RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
	RFC3339     = "2006-01-02T15:04:05Z07:00"
	RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
	Kitchen     = "3:04PM"
	// Handy time stamps.
	Stamp      = "Jan _2 15:04:05"
	StampMilli = "Jan _2 15:04:05.000"
	StampMicro = "Jan _2 15:04:05.000000"
	StampNano  = "Jan _2 15:04:05.000000000"
)     

注意: galang 中指定的特定时间格式为 "2006-01-02 15:04:05 -0700 MST", 为了记忆方便,按照美式时间格式 月日时分秒年 外加时区 排列起来依次是 01/02 03:04:05PM ‘06 -0700,刚开始使用时需要注意。

3 to time stamp

方法

 func (t Time) Unix() int64
 func (t Time) UnixNano() int64
$ cat time2.go
package main
import "fmt"
import "time"
func main() {
 fmt.Println(time.Now().Unix())
 // 获取指定日期的时间戳
  dt, _ := time.Parse("2016-01-02 15:04:05", "2018-04-23 12:24:51")
  fmt.Println(dt.Unix())
  fmt.Println(time.Date(2018, 1,2,15,30,10,0, time.Local).Unix())
}
[root@localhost time]$ go run time2.go 
1582797528
-62135596800
1514878210

4 其他

time 包还提供了一些常用的方法

  func (t Time) Date() (year int, month Month, day int)
  func (t Time) Clock() (hour, min, sec int)
  func (t Time) Year() int
  func (t Time) Month() Month
  func (t Time) Day() int
  func (t Time) Hour() int
  func (t Time) Minute() int
  func (t Time) Second() int
  func (t Time) Nanosecond() int
  func (t Time) YearDay() int
  func (t Time) Weekday() Weekday
  func (t Time) ISOWeek() (year, week int)
  func (t Time) IsZero() bool
  func (t Time) Local() Time
  func (t Time) Location() *Location
  func (t Time) Zone() (name string, offset int)
  func (t Time) Unix() int64

5 时间段(Duartion)

介绍完了时间点,我们再来介绍时间段,即 Duartion 类型, 我们业务也是很常用的类型。

   func (d Duration) Hours() float64
   func (d Duration) Minutes() float64
   func (d Duration) Seconds() float64
   func (d Duration) Nanoseconds() int64
   func (d Duration) Round(m Duration) Duration         // 四舍五入
   func (d Duration) Truncate(m Duration) Duration      // 向下取整
  $ cat time3.go
package main
import "fmt"
import "time"

func main() {
    tp, _ := time.ParseDuration("1.5s")
    fmt.Println(tp.Truncate(1000), tp.Seconds(), tp.Nanoseconds())

}
[root@localhost time]# go run time3.go 
1.5s 1.5 1500000000

6 时区(Location)

$  cat time4.go 
package main
import "fmt"
import "time"

func main() {
   loc1, err := time.LoadLocation("") 
   if err != nil {
         fmt.Print("time location error")
	}
   fmt.Println(time.Date(2018,1,1,12,0,0,0, loc1))

   loc2, err := time.LoadLocation("Local")
   fmt.Println(time.Date(2018,1,1,12,0,0,0, loc2))

   local, _ := time.LoadLocation("America/Los_Angeles")
   fmt.Println(time.Date(2018,1,1,12,0,0,0, local))

}
[root@localhost time]# go run time4.go
2018-01-01 12:00:00 +0000 UTC
2018-01-01 12:00:00 +0800 CST
2018-01-01 12:00:00 -0800 PST

7 时间运算

语法:

//通过字符串,默认UTC时区初始化Time
func Parse(layout, value string) (Time, error) 
//通过字符串,指定时区来初始化Time
func ParseInLocation(layout, value string, loc *Location) (Time, error) 

//通过unix 标准时间初始化Time
func Unix(sec int64, nsec int64) Time 
$  cat time5.go 
package main

import (
	"fmt"
	"strings"
	"time"
)

func main() {
	now := time.Now()
	
	m, _ := time.ParseDuration("-1m")
	m1 := now.Add(m)
	fmt.Println(m1)

	// 8个小时前
	h, _ := time.ParseDuration("-1h")
	h1 := now.Add(8 * h)
	fmt.Println(h1)

	// 一天前
	d, _ := time.ParseDuration("-24h")
	d1 := now.Add(d)
	fmt.Println(d1)

	printSplit(50)

	// 10分钟后
	mm, _ := time.ParseDuration("1m")
	mm1 := now.Add(mm)
	fmt.Println(mm1)

	// 8小时后
	hh, _ := time.ParseDuration("1h")
	hh1 := now.Add(hh)
	fmt.Println(hh1)

	// 一天后
	dd, _ := time.ParseDuration("24h")
	dd1 := now.Add(dd)
	fmt.Println(dd1)

	printSplit(50)

	// Sub 计算两个时间差
	subM := now.Sub(m1)
	fmt.Println(subM.Minutes(), "分钟")

	sumH := now.Sub(h1)
	fmt.Println(sumH.Hours(), "小时")

	sumD := now.Sub(d1)
	fmt.Printf("%v 天\n", sumD.Hours()/24)

}

func printSplit(count int) {
	fmt.Println(strings.Repeat("#", count))
}

输出:

2020-02-27 18:21:03.839940187 +0800 CST m=-59.999903567
2020-02-27 10:22:03.839940187 +0800 CST m=-28799.999903567
2020-02-26 18:22:03.839940187 +0800 CST m=-86399.999903567
##################################################
2020-02-27 18:23:03.839940187 +0800 CST m=+60.000096433
2020-02-27 19:22:03.839940187 +0800 CST m=+3600.000096433
2020-02-28 18:22:03.839940187 +0800 CST m=+86400.000096433
##################################################
1 分钟
8 小时
1

```go
$ cat time6.go 
package main
import "fmt"
import "time"

func main() {
 dt1 := time.Date(2018, 1, 10, 0, 0, 1, 100, time.Local)
 dt2 := time.Date(2018, 1, 9, 23, 59, 22, 100, time.Local)
 // 不用关注时区,go会转换成时间戳进行计算
 fmt.Println(dt1.Sub(dt2))  
}
[root@localhost time]# go run time6.go 
39s
$ cat time7.go
package main
import "fmt"
import "time"

func main() {

 local, _ := time.LoadLocation("America/Los_Angeles")
    timeFormat := "2006-01-02 15:04:05"
    //func Unix(sec int64, nsec int64) Time {
    time1 := time.Unix(1480390585, 0)                                                     //通过unix标准时间的秒,纳秒设置时间
    time2, _ := time.ParseInLocation(timeFormat, "2016-11-28 19:36:25", local) //洛杉矶时间
    fmt.Println(time1.In(local).Format(timeFormat))
    fmt.Println(time2.In(local).Format(timeFormat))
    chinaLocal, _ := time.LoadLocation("Local")//运行时,该服务器必须设置为中国时区,否则最好是采用"Asia/Chongqing"之类具体的参数。
    fmt.Println(time2.In(chinaLocal).Format(timeFormat))

}
[root@localhost time]# go run  time7.go
2016-11-28 19:36:25
2016-11-28 19:36:25
2016-11-29 11:36:25

8 比较两个时间点

$ cat  time8.go
package main
import "fmt"
import "time"

func main() {

dt := time.Date(2018, 1, 10, 0, 0, 1, 100, time.Local)
      fmt.Println(time.Now().After(dt))     // true
      fmt.Println(time.Now().Before(dt))    // false

      // 是否相等 判断两个时间点是否相等时推荐使用 Equal 函数
      fmt.Println(dt.Equal(time.Now()))

}
[root@localhost time]# go run  time8.go
true
false
false

9 Ticker类型

Ticker 类型包含一个 channel,有时我们会遇到每隔一段时间执行的业务(比如设置心跳时间等),就可以用它来处理,这是一个重复的过程.
time9.go

package main
import (
        "fmt"

        "time"
)
func main() {
        d := time.Duration(time.Second*2)
        t := time.NewTicker(d)
        defer t.Stop()
        for {
                <- t.C
                fmt.Println("timeout...")
        }
}

[root@localhost time]# go run time9.go 
timeout...
timeout...
timeout...
timeout...

10 Timer类型

Timer 类型用来代表一个单独的事件,当设置的时间过期后,发送当前的时间到 channel, 我们可以通过以下两种方式来创建

  func AfterFunc(d Duration, f func()) *Timer   // 指定一段时间后指定的函数
  func NewTimer(d Duration) *Timer   

以上两函数都可以使用 Reset, 这个有个需要注意的地方是使用 Reset 时需要确保 t.C 通道被释放时才能调用,以防止发生资源竞争的问题,可通过以下方式解决

  if !t.Stop() {
        <-t.C
  }
  t.Reset(d)

time10.go

package main
import (
        "fmt"
        "time"
)
func main() {
        d := time.Duration(time.Second*2)
        t := time.NewTimer(d)
        defer t.Stop()
        for {
                <- t.C
                fmt.Println("timeout...")
		// need reset
		t.Reset(time.Second*2)
        }
}
[root@localhost time]# go run time10.go 
timeout...
timeout...

11 总结

package main

import (
        "fmt"
        "time"
)

func main(){
        //time.Time代表一个纳秒精度的时间点
        var t time.Time

        //返回当前时间:2017-10-23 19:16:25.3599073 +0800 CST
        t = time.Now()
        fmt.Printf("%v\n", t)

        //返回所在时区:Local
        fmt.Printf("%v\n", t.Location())

        //返回UTC时间和UTC时区:2017-10-23 11:16:25.3599073 +0000 UTC UTC
        fmt.Printf("%v %v\n", t.UTC(), t.UTC().Location())

        //同上,In()返回指定时区的时间:2017-10-23 11:16:25.3599073 +0000 UTC UTC
        fmt.Printf("%v %v\n", t.In(time.UTC), t.In(time.UTC).Location())

        //返回当地时区的时间:2017-10-23 19:16:25.3599073 +0800 CST Local
        fmt.Printf("%v %v\n", t.Local(), t.Local().Location())

        //根据时间戳返回本地时间,参数分别表示秒数和纳秒数
        //2017-02-23 00:13:30 +0800 CST
        t2 := time.Unix(1487780010, 0)
        fmt.Println(t2)

        //根据指定时间返回time.Time,分别指定年,月,日,时,分,秒,纳秒,时区
        //2017-05-26 15:30:20 +0800 CST
        t3 := time.Date(2017, time.Month(5), 26, 15, 30, 20, 0, t.Location())
        fmt.Println(t3)

        //格式化输出时间:2017-10-23 19:16:25
        t4 := time.Now()
        fmt.Println(t4.Format("2006-01-02 15:04:05"))

        //获取时间信息:2017 October 23
        t5 := time.Now()

        //返回日期:2017 October 23
        fmt.Println(t5.Date())

        //返回年:2017
        fmt.Println(t5.Year())

        //返回月:October
        fmt.Println(t5.Month())

        //返回日:23
        fmt.Println(t5.Day())

        //返回星期:Monday
        fmt.Println(t5.Weekday())

        //返回ISO 9601标准下的年份和星期编号:2017 43
        fmt.Println(t5.ISOWeek())

        //返回时分秒:19 16 25
        fmt.Println(t5.Clock())

        //返回小时:19
        fmt.Println(t5.Hour())

        //返回分钟:16
        fmt.Println(t5.Minute())

        //返回秒:25
        fmt.Println(t5.Second())

        //返回纳秒:381921400
        fmt.Println(t5.Nanosecond())

        //返回一年中对应的天:296
        fmt.Println(t5.YearDay())

        //返回时区的规范名,时区相对于UTC的时间偏移量(秒):CST 28800
        fmt.Println(t5.Zone())

        //返回时间戳:1508757385
        fmt.Println(t5.Unix())

        //返回纳秒时间戳:1508757385381921400
        fmt.Println(t5.UnixNano())

        //时间的比较与计算
        t6 := time.Now()

        //是否零时时间:false
        fmt.Println(t6.IsZero())

        //t6时间在t5时间之后,返回真:true
        fmt.Println(t6.After(t5))

        //t5时间在t6时间之前,返回真:true
        fmt.Println(t5.Before(t6))

        //时间是否相同:true
        fmt.Println(t6.Equal(t6))

        //返回t6加上纳秒的时间:2017-10-23 19:16:25.383933 +0800 CST
        fmt.Println(t6.Add(10000))

        //返回两个时间之差的纳秒数:2.0016ms
        fmt.Println(t6.Sub(t5))

        //返回t6加1年,1月,1天的时间:2018-11-24 19:16:25.383923 +0800 CST
        fmt.Println(t6.AddDate(1, 1, 1))

        //时间的序列化
        t7 := time.Now()

        //序列化二进制
        bin, _ := t7.MarshalBinary()

        //反序列化二进制:2017-10-23 19:16:25.383923 +0800 CST
        t7.UnmarshalBinary(bin)
        fmt.Println(t7)

        //序列化json:"2017-10-23T19:16:25.383923+08:00"
        json, _ := t7.MarshalJSON()
        fmt.Println(string(json))

        //反序列化json:2017-10-23 19:16:25.383923 +0800 CST
        t7.UnmarshalJSON(json)
        fmt.Println(t7)

        //序列化文本:2017-10-23T19:16:25.383923+08:00
        txt, _ := t7.MarshalText()
        fmt.Println(string(txt))

        //反序列化文本:2017-10-23 19:16:25.383923 +0800 CST
        t7.UnmarshalText(txt)
        fmt.Println(t7)

        //gob编码:2017-10-23 19:16:25.383923 +0800 CST
        gob, _ := t7.GobEncode()
        t7.GobDecode(gob)
        fmt.Println(t7)

        //时间段time.Duration
        dur := time.Duration(6666666600000)

        //返回字符串表示:1h51m6.6666s
        fmt.Println(dur.String())

        //返回小时表示:1.8518518333333334
        fmt.Println(dur.Hours())

        //返回分钟表示:111.11111
        fmt.Println(dur.Minutes())

        //返回秒表示:6666.6666
        fmt.Println(dur.Seconds())

        //返回纳秒表示:6666666600000
        fmt.Println(dur.Nanoseconds())

        //时区time.Location,返回时区名:local
        fmt.Println(time.Local.String())

        //通过地点名和时间偏移量返回时区:Shanghai
        fmt.Println(time.FixedZone("Shanghai", 800))

        //通过给定时区名称,返回时区:Asia/Shanghai
        loc, _ := time.LoadLocation("Asia/Shanghai")
        fmt.Println(loc)

        //阻塞当前进程3秒
        time.Sleep(time.Second * 3)

        //定时器time.Timer,创建一个1秒后触发定时器
        timer1 := time.NewTimer(time.Second * 1)
        <-timer1.C
        fmt.Println("timer1 end")

        //1秒后运行函数
        time.AfterFunc(time.Second * 1, func() {
                fmt.Println("wait 1 second")
        })
        time.Sleep(time.Second * 3)

        //打点器time.Ticker,创建一个打点器,在固定1秒内重复执行
        ticker := time.NewTicker(time.Second)
        num := 1
        for {
                if num > 5 {
                        ticker.Stop()
                        break
                }
                select {
                        case <-ticker.C:
                                num++
                                fmt.Println("1 second...")
                }
        }
}

12 实用场景

t := 5
time.Sleep(time.Second * 5) // ok 正常
time.Sleep(time.Second * t)  // error 出现标题错误提示
time.Sleep(time.Second * time.Duration(t)) // ok 应该这样转

13 时间的格式化和解析

package main

import "fmt"
import "time"

func main() {
    p := fmt.Println

    // 这里是一个基本的按照 RFC3339 进行格式化的例子,使用
    // 对应模式常量。
    t := time.Now()
    p(t.Format(time.RFC3339))

    // 时间解析使用同 `Format` 相同的形式值。
    t1, e := time.Parse(
        time.RFC3339,
        "2012-11-01T22:08:41+00:00")
    p(t1)

    // `Format``Parse` 使用基于例子的形式来决定日期格式,
    // 一般你只要使用 `time` 包中提供的模式常量就行了,但是你
    // 也可以实现自定义模式。模式必须使用时间 `Mon Jan 2 15:04:05 MST 2006`
    // 来指定给定时间/字符串的格式化/解析方式。时间一定要按照
    // 如下所示:2006为年,15 为小时,Monday 代表星期几,等等。
    p(t.Format("3:04PM"))
    p(t.Format("Mon Jan _2 15:04:05 2006"))
    p(t.Format("2006-01-02T15:04:05.999999-07:00"))
    form := "3 04 PM"
    t2, e := time.Parse(form, "8 41 PM")
    p(t2)

    // 对于纯数字表示的时间,你也可以使用标准的格式化字
    // 符串来提出出时间值得组成。
    fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n",
        t.Year(), t.Month(), t.Day(),
        t.Hour(), t.Minute(), t.Second())

    // `Parse` 函数在输入的时间格式不正确是会返回一个
    // 错误。
    ansic := "Mon Jan _2 15:04:05 2006"
    _, e = time.Parse(ansic, "8:41PM")
    p(e)
}

输出结果:

2020-11-23T11:06:33+08:00
2012-11-01 22:08:41 +0000 +0000
11:06AM
Mon Nov 23 11:06:33 2020       
2020-11-23T11:06:33.251533+08:00
0000-01-01 20:41:00 +0000 UTC
2020-11-23T11:06:33-00:00
parsing time "8:41PM" as "Mon Jan _2 15:04:05 2006": cannot parse "8:41PM" as "Mon"

参考连接:
https://juejin.im/post/5ae32a8651882567105f7dd3

http://static.markbest.site/blog/92.html
Time 类型详解