go 中 time 包的使用

time 包中的类型

time 包中定义了以下时间类型:

time.Time{}

时间类型,包含了秒和纳秒以及 Location:

type Time struct {
    wall uint64 // 秒
    ext  int64  // 纳秒
    loc *Location
}

time.Month()

月份,定义了十二个月份的常量:

type Month int

const (
    January Month = 1 + iota
    February
    March
    April
    May
    June
    July
    August
    September
    October
    November
    December
)

time.Weekday

月份,定义了十二个月的常量:

type Weekday int

const (
    Sunday Weekday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

time.Duration

持续时间,定义了以下持续时间类型,多用于时间的加减:

// 持续时间表示两个瞬间之间经过的时间,以int64纳秒为单位。
// 该表示将最大可表示持续时间限制为大约290年。
type Duration int64

const (
    minDuration Duration = -1 << 63
    maxDuration Duration = 1<<63 - 1
)

const (
    Nanosecond  Duration = 1
    Microsecond          = 1000 * Nanosecond
    Millisecond          = 1000 * Microsecond
    Second               = 1000 * Millisecond
    Minute               = 60 * Second
    Hour                 = 60 * Minute
)

time.Location

在 time 包里有两个时区变量:

  • time.UTC:utc 时间
  • time.Local:本地时间

另外,还提供了一个 FixedZone 函数,用于设置时区名,以及与 UTC0 的时间偏差,返回 Location:

func FixedZone(name string, offset int) *Location {
    l := &Location{
        name:       name,
        zone:       []zone{{name, offset, false}},
        tx:         []zoneTrans{{alpha, 0, false, false}},
        cacheStart: alpha,
        cacheEnd:   omega,
    }
    l.cacheZone = &l.zone[0]
    return l
}

time.Timer 定时器

传达一次到期事件:

type Timer struct {
    C <-chan Time
    r runtimeTimer
}

time.Ticker 断续器

周期性的传达到期事件的装置。

与定时器的区别:定时器只会传达一次到期事件,断续器会持续工作直到停止

type Ticker struct {
    C <-chan Time // The channel on which the ticks are delivered.
    r runtimeTimer
}

time 包中的常用函数

Time 类型下的常用函数

  • After(u Time) bool:时间类型比较,是否在 Time 之后
  • Before(u Time) bool:时间类型比较,是否在 Time 之前
  • Equal(u Time) bool:比较两个时间是否相等
  • IsZero() bool:判断时间是否为零值,如果 sec 和 nsec 两个属性都是 0 的话,则该时间类型就是 0
  • **Date() (yeat int,month Month,day int)**:返回年,月,日三个参数
  • Year() int:返回年份
  • Month() int:返回月份
  • Day() int:返回多少号
  • Weekday() Weekday:返回星期几,是 Weekday 类型
  • **ISOWeek() (year,week int)**:返回年份以及该天在这年的第几周
  • **Clock() (hour,min,sec int)**:返回小时,分钟,秒
  • Hour() int:返回小时
  • Minute() int:返回分钟
  • Second() int:返回秒数
  • Nanosecond() int:返回纳秒
  • Unix() int64:返回时间戳,自 1970 年 1 月 1 号到现在的秒数
  • UnixNano() int64:返回时间戳,包含纳秒数
  • Format(layout string) string:将时间转换为一个格式
  • Add(d Duration) Time:为一个时间添加一段时间,可以精确到纳秒
  • Sub(u Time) Duration:计算两个时间的时间差
  • AddDate(years int,months int,days int) Time:添加时间,以年月日为参数
  • UTC() Time:设置 location 为 UTC,然后返回时间(相当于 utc0,比中国晚 8 个小时)
  • Local() Time:设置 location 为本地时间,就是电脑时间
  • **In(loc *Location) Time:**设置 location 为指定的 location
  • **Location() *Location**:获取时间的 Location,如果是 nic,则返回 UTC,如果为空,则代表本地
  • **Zone() (name string,offset int)**:返回时区,以及与 utc 的时间偏差

其他常用函数

  • **time.Now()**:返回当前时间,返回 Time 类型
  • time.Sleep(d Duration):表示沉睡多少时间,此时处于阻塞状态
  • time.Since(t Time) Duration:返回与当前时间的时间差,t 一般为过去时间
  • time.Unix(sec int64,nesc int64) Time:根据秒数和纳秒,返回 Time 类型。如只有一个参数,则另外一个参数默认为 0。
  • time.Until(t Time) Duration:计算 t 到当前时间的时间差,t 一般为未来时间
  • time.After(d Duration) <-chan Time:返回一个 time.C 这个管道,d 时间间隔后会在此管道中放入一个时间点(time.Now()
  • **time.AfterFunc(d Duration,f func()) *Timer**:将一个时间间隔和一个函数传给 AfterFunc 之后,间隔时间过后将执行传入的函数,只执行一遍
  • **time.NewTimer(d Duration) *Timer:**创建一个新的定时器,定时时间为 d
  • **time.Parse(layout,value string) (Time,error)**:将字符串转化为 Time 类型。
  • **time.ParseDuration(s string) (Duration,error)**:将字符串(”ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”)转化为 Duration 类型
  • **time.ParseInLocation(layout,value string,loc *Location) (Time,error)**:ParseInLocation 类似于Parse,但在两个重要方面有所不同:
    • 首先,在没有时区信息的情况下,Parse将时间解释为 UTC,ParseInLocation 将时间解释为给定位置。
    • 其次,当给定区域偏移量或缩写时,Parse 会尝试将其与本地位置进行匹配。 ParseInLocation 使用给定的位置。
  • **time.NewTicker(d Duration) *Ticker**:创建一个新的 Ticker
  • time.Tick(d Duration) <-chan Time:返回一个 time.C 管道,d 时间间隔后会在此管道中放入一个时间点(time.Now())
  • **time.Date(yeat int,month Month,day,hour,min,sec,nsec int,lo *Location) Time**:设置年月日,返回 Time 类型
  • **time.LoadLocation(name string) (*Location,error)**:返回给定名称的位置
  • **time.FixedZone(name string,offset int) *Location:**返回给定名称和偏移量的位置

time 包的实际用例

创建时间

获取当前时间

now := time.Now()

创建指定的时间

func main() {
    date := time.Date(2008,8,8,21,22,23,456234,time.UTC)
    fmt.Printf("date is %s\n", date)
    date = time.Now().UTC()
    fmt.Printf("current date is %s",date)
}

输出如下:

date is 2008-08-08 21:22:23.000456234 +0000 UTC
current date is 2021-03-23 06:58:28.054182 +0000 UTC

纳秒/毫秒/秒转换

fmt.Println(ns / 1e6) // 纳秒转毫秒
fmt.Println(ns / 1e9) // 纳秒转秒

时间格式化(time 与 string 相互转换)

Go 不使用 yyyy-mm-dd 布局来格式化时间。而是通过传入一个 string 类型的 layout 布局参数,该参数定义的时间节点为:

// 2006-01-02 15:04:05
Mon Jan 2 15:04:05 -0700 MST 2006
const (
    // 定义的时间节点必须符合布局参数定义
    layoutISO = "2006-01-02 15:04:05"
    layoutUS = "January 2, 2006"
)

func main() {
    // string 转 time
    date := "2021-03-23"
    t, _ := time.Parse(layoutISO, date)
    fmt.Println(t)

    // time 转 string
    now := time.Now()
    fmt.Println(now.Format(layoutUS))
    fmt.Println(now.Format(layoutISO))
}
  • time.Parse :解析日期字符串
  • Format:格式化 time.Time

函数签名如下:

func Parse(layout, value string) (Time, error) 
func (t Time) Format(layout string) string 

另外,Go 在 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"
)

对时间进行加减运算

加法

func main() {
    date := time.Date(2008,8,8,21,22,23,456234,time.UTC)
    nextDate := date.AddDate(0,0,1)
    nextDateByHour := date.Add(+time.Hour * 24)
    previousDate := date.Add(-time.Hour * 24)
    
    fmt.Printf("date is %s\n",date)
    fmt.Printf("next date is %s\n",nextDate)
    fmt.Printf("next date by add hour is %s\n",nextDateByHour)
    fmt.Printf("privious date by add hour is %s\n",previousDate)
}

输出结果如下:

date is 2008-08-08 21:22:23.000456234 +0000 UTC
next date is 2008-08-09 21:22:23.000456234 +0000 UTC
next date by add hour is 2008-08-09 21:22:23.000456234 +0000 UTC
privious date by add hour is 2008-08-07 21:22:23.000456234 +0000 UTC

减法

func main() {
    start := time.Now()
    time.Sleep(1*time.Second)
    stop := time.Now()

    cost := stop.Sub(start)
    fmt.Printf("The call took %v to run.\n",cost)
}

输出结果如下:

The call took 1.000541262s to run.

时间戳与Time相互转换

Time 类具有 Unix() 方法,可以将时间转换为 glang 中的 UNIX 时间戳:

func main() {
    now := time.Now().Unix() // 秒数
    nowNano := time.Now().UnixNano() // 纳秒数

    // now is 1616484297
    fmt.Printf("now is %v\n",now)
    // nowNano is 1616484297027772000
    fmt.Printf("nowNano is %v\n",nowNano)
}

Unix 时间戳转 Time 格式:通过 time.Unix 函数

func main() {
    now := time.Unix(time.Now().Unix(), 0)
    fmt.Printf("now is %v\n",now)
}

输出结果如下:

now is 2021-03-23 15:32:41 +0800 CST

在本地和其他时区使用时间戳获取当前日期和时间

LoadLocatio 返回具有给定名称的 Location

func main() {
    t := time.Now()
    fmt.Println("Location: ", t.Location(), " Time: ", t)

    location, _ := time.LoadLocation("America/New_York")

    // 转换时区
    tInAmerica := t.In(location)
    fmt.Println("Location: ", location, " Time: ", tInAmerica)

    location, _ = time.LoadLocation("Asia/Shanghai")
    tInShanghai := t.In(location)
    fmt.Println("Location: ", location, " Time: ", tInShanghai)
}

输出结果:

Location:  Local  Time:  2021-03-23 21:46:37.255412 +0800 CST m=+0.000090396
Location:  America/New_York  Time:  2021-03-23 09:46:37.255412 -0400 EDT
Location:  Asia/Shanghai  Time:  2021-03-23 21:46:37.255412 +0800 CST

获取 Weekday 和 Yearday

Weekday 返回由 t 指定的星期几。Yearday 返回由 t 指定的一年中的第几天,非闰年范围为 [1,365],闰年为 [1,366]

func main() {
    t, _ := time.Parse("2006 01 02 15 04", "2015 11 11 16 50")
    fmt.Println(t.YearDay()) // 315
    fmt.Println(t.Weekday()) // Wednesday

    t, _ = time.Parse("2006 01 02 15 04", "2011 01 01 0 00")
    fmt.Println(t.YearDay()) // 1
    fmt.Println(t.Weekday()) // Saturday
}

日期时间的前后关系判断

func main() {
    t1 := time.Now()
    t2 := t1.Add(time.Minute)
    t3 := t1.Add(-time.Minute)
    if t1.Before(t2) {
        fmt.Println("t1 is before t2")
    } else {
        fmt.Println("t1 is after t2")
    }
    fmt.Println(time.Since(t3))
}

输出结果如下:

t1 is before t2
1m0.000028295s

定时器

func main() {
    go func() {
        timer := time.NewTimer(2 * time.Second)
        for t := range timer.C:
                fmt.Println("timer fired")
            }
        }
    }()

    time.Sleep(3 * time.Second)
}

断续器(每个固定的时间执行一次)

func main() {
    ticker := time.NewTicker(1 * time.Second)
    go func() {
        for t := range ticker.C:
                fmt.Println("ticker at: ", t)
            }
        }
    }()

    time.Sleep(10 * time.Second)
}
ticker at:  2021-03-23 22:19:49.743301 +0800 CST m=+1.003682073
ticker at:  2021-03-23 22:19:50.741223 +0800 CST m=+2.001600968
ticker at:  2021-03-23 22:19:51.744963 +0800 CST m=+3.005338145
ticker at:  2021-03-23 22:19:52.742212 +0800 CST m=+4.002584520
ticker at:  2021-03-23 22:19:53.741429 +0800 CST m=+5.001799684
ticker at:  2021-03-23 22:19:54.742483 +0800 CST m=+6.002850379
ticker at:  2021-03-23 22:19:55.741314 +0800 CST m=+7.001678696
ticker at:  2021-03-23 22:19:56.743402 +0800 CST m=+8.003764744
ticker at:  2021-03-23 22:19:57.740211 +0800 CST m=+9.000570923

参考资料

go time包的使用

Golang如何使用time包