Go内置的time
包提供了时间显示和测量的函数
- 时间可分为时间点和时间段,为此Go提供了两种基础数据类型
time.Time
和time.Duration
。 - 所有与时间相关的业务都是基于时间点而衍生的,两个时间点组成一个时间段。
- 时间包括时间值和时区,没有包含时区信息的时间是不完整的、有歧义的。
- 日历的计算采用的是公历
- Go提供针对某些特定业务提供了诸如
go.Location
时区、go.Timer
定时器...
功能 | 描述 |
---|---|
time.Time | 时间点 |
time.Duration | 时间段 |
time.Location | 时区 |
time.Timer | 定时器 |
time.Ticker | 周期触发定时的计时器 |
time.C | 存放时间点的管道 |
时间类型
time
包提供了一个数据类型time.Time
用来表示时间,时间一般包含时间值和时区两部分,时间值是一个纳秒精度的时间点。
package time
type Time struct {
wall uint64
ext int64
loc *Location
}
time.Time
结构包含三个字段:
字段 | 类型 | 描述 |
---|---|---|
wall | uint64 | 墙上时钟,表示距离公元1年1月1日00:00:00UTC的秒数。 |
ext | int64 | 时间模式,表示纳秒。 |
loc | *time.Location | 时区,处理偏移量。不同时区对应的时间也不同。 |
最准确的时间计算是使用“原子振荡周期”所计算的物理时钟(Atomic Clock,原子钟),又被定义为标准时间(International Atomic Time)。常用的UTC(Universal Time Coordinated,世界协调时间)就是利用原子钟为基准定义出来的,UTC标准时间以GMT(Greenwich Mean Time,格林威治时间)这个时区为基准。
本地时间 = UTC + 时区差
北京时间 = UTF + 8个小时
操作系统中存在两个不同的时钟,分别是单调时钟(monotonic time)、墙上时钟(wall time/real time)
- 单调时钟是本机硬件的节拍数,不同机器没有可比性。由于石英钟本身的误差,时间会有闰秒等原因,因此单调时钟与墙上时钟是不一致的。
- 墙上时钟又称为时钟时间,是进程运行的时钟总量,其值与系统中同时运行的进程数量有关。进程从开始运行到结束,时钟走过的时间,包括进程在阻塞和等待状态的时间。
墙上时钟 | 大小 |
---|---|
flag | 1位 |
时间戳 | 33位 |
纳秒 | 30位 |
时间模式分为:是否包含单调时钟、外部输入的时间
- 若时间模式
ext
包含单调时钟,则wall
字段中会存在一个时间戳秒代表墙上时钟和一个时间戳纳秒代表墙上时钟。 - 若时间模式
ext
不包含单调时钟,则wall
字段中会包含一个时间戳纳秒代表墙上时钟,ext
字段则包含一个时间戳秒代表墙上时钟。
当前时间
-
time.Now()
会返回当前本地时间对象
func Now() Time
例如:获取当前本地时间信息
t := time.Now()
fmt.Println(t) // 2021-12-28 13:10:36.5987998 +0800 CST m=+0.003958401
从时间对象中获取时间分量信息
时间分量 | 描述 |
---|---|
t.Year() | 年 |
t.Month() | 月 |
t.Day() | 日 |
t.Hour() | 小时数 |
t.Minute() | 分钟 |
t.Second() | 秒数 |
t.Nanosecond() | 纳秒,1毫秒 = 1000纳秒, 1秒 = 1000毫秒 |
t.Weekday() | 星期几 |
t.YearDay() | 一年中的第n天 |
t.Zone() | 获取时区 |
t := time.Now()
fmt.Println(t) // 2021-12-28 13:43:30.9266336 +0800 CST m=+0.004069101
fmt.Printf("Year = %d\n", t.Year()) // 2021
fmt.Printf("Month = %d\n", t.Month()) // 12
fmt.Printf("Day = %d\n", t.Day()) // 28
fmt.Printf("Hour = %d\n", t.Hour()) // 13
fmt.Printf("Minute = %d\n", t.Minute()) // 43
fmt.Printf("Second = %d\n", t.Second()) // 30
fmt.Printf("NanoSecond = %d\n", t.Nanosecond()) // 926633600
fmt.Printf("Weekday = %d\n", t.Weekday()) // 2
fmt.Printf("YearDay = %d\n", t.YearDay()) // 362
fmt.Println(t.Zone()) // CST 28800
时间解析
time.Parse()
会将字符串类型的日期时间转换为UTC标准的Time类型
package time
func Parse(layout, value string) (Time, error)
例如:将字符串"2021-01-04 12:00:00"转换为标准UTC的time.Time
类型
t, err := time.Parse("2006-01-02 15:04:05", "2021-01-04 12:00:00")
if err != nil {
panic(err)
}
fmt.Printf("%T\n", t) // time.Time
fmt.Printf("%+v\n", t) // 2021-01-04 12:00:00 +0000 UTC
时间戳
UNIX时间戳(UNIX Timestamp)
- Unix时间又称为POSIX时间,是UNIX或类UNIX操作系统使用的时间表示方式。
- Unix时间表示从UTC的1970年1月1日0时0分0秒开始到现在的总秒数,不考虑闰秒。
func (t Time) Unix() int64
func (t Time) UnixNano() int64
时间戳 | 单位 | 长度 |
---|---|---|
t.Unix() | 秒 | 10位 |
t.UnixNano() | 纳秒 | 19位 |
s := t.Unix()
ns := t.UnixNano()
ms := t.UnixNano() / 1e6
fmt.Printf("timestamp = %d, unixnano = %d, ms = %d\n", s, ns, ms)
timestamp = 1618738171, unixnano = 1618738171513428200, ms = 1618738171513
19位纳秒转13位毫秒
ms := t.UnixNano() / 1e6
fmt.Println(ms) // 1618737494948
-
time.Unix()
函数可将时间戳转换为时间点对象
func time.Unix(sec int64, nsec int64) time.Time
时间格式
time.Format()
用于获取时间并格式化为字符串,格式化占位符2006-01-02 15:04:05
表示GoLang诞生时间,此处为固定写法。
func (t Time) Format(layout string) string
例如:将时间点对象转换为指定格式的字符串
t := time.Now()
fmt.Println(t)//2021-04-18 17:53:23.8149167 +0800 CST m=+0.002995001
str := t.Format("2006-01-02 15:04:05")
fmt.Println(str)//2021-04-18 17:53:23
例如:将时间戳转换为指定格式的字符串
ts := time.Now().Unix()
fmt.Println(ts)//1618739719
str := time.Unix(ts,0).Format("2006-01-02 15:04:05")
fmt.Println(str)//2021-04-18 17:55:19
标准时间
- UTC(Coordinated Universal Time,协调世界时)是最主要的世界时间标准,它以原子时秒长为基础,在时刻上尽量接近于格林尼治标准时间(Greenwich Mean Time,GMT)。
- UTC是世界上调节时钟和时间的主要时间标准,它与0度经线的平太阳时相差不超过1秒,且不遵守夏令时。
- UTC是最接近格林威治标准时间(GMT)的替代时间系统之一,对于大多数用途来说,UTC时间被认为与GMT时间互换,但GMT时间已不再被科学界所确定。
func (t Time) UTC() Time
例如:将GMT格林威治时间转换为UTC时间
t := time.Now()
fmt.Println(t)//2021-04-18 17:57:51.7228428 +0800 CST m=+0.002020201
utc := t.UTC()
fmt.Println(utc)//2021-04-18 09:57:51.7228428 +0000 UTC
时区
时间其实包括时间值和时区,没有时区信息的时间是不完整的。使用没有时区的非标准时间表示格式是有隐患的,由于解析时会根据使用场景的默认设置,如系统时区、数据库默认时区等,可能会引发事故。因此需确保服务器系统、数据库、应用程序时间统一的时区。
time
包存在两个时区变量分别是time.UTC
即UTC时间、time.Local
本地时间
time.Location()
函数返回时间点对象的地点和时区信息
func (t Time) Location() *Location
例如:获取时间点对应的时区
t := time.Now()
loc := t.Location()
fmt.Println(loc)//Local
time.LoadLocation()
用于获取给定名字创建的时区
func time.LoadLocation(name string) (*time.Location, error)
例如:获取服务器所在时区
loc,err := time.LoadLocation("Asia/Shanghai")
if err!=nil {
println(err)
}
println(loc)//0xc0000c2000
是否能够拿到时区取决于机器本地的zoneinfo
文件,time.LoadLocation()
函数将会返回一个*time.Location
指针对象。
计时器
time.Ticker
time.Ticker
是一个周期触发定时的计时器,一旦被定义每隔一段时间会自动触发。
type Ticker struct {
C <-chan Time // 周期性传递时间信息的通道
r runtimeTimer
}
time.Ticker
会按照一个指定的时间间隔重复的向通道C
发送系统当前时间值,通道的接收者可以按固定的时间间隔从通道中读取事件。
//实现原理
go func(t *time.Ticker){
for{
select{
case <-t.C:
fmt.Printf("ticker: run\n")
}
}
}(ticker)
在goroutine
周期性执行一些事务时非常有用,比如状态状态日志、输出、计算等。
例如:
ticker := time.NewTicker(time.Second * 3)
defer ticker.Stop()
fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
time.Sleep(time.Second * 4)
for{
select{
case <-ticker.C:
fmt.Println(time.Now())
}
}
time.NewTicker
NewTicker
方法用于返回一个新的Ticker,Ticker中包含一个通道字段C
会每隔时间段d
就向该通道发送当前的时间。
func time.NewTicker(d time.Duration) *time.Ticker
- 时间间隔
d
的单位为纳秒(ns, int64),在工厂函数time.NewTicker
中以time.Duration
类型的参数传入。 -
*Ticker
指向定时器的指针
例如:定时执行,循环执行不退出。
//创建计时器,每隔1秒后定时器会向channel发送一个事件,包含当前时间。
ticker := time.NewTicker(time.Second * 1)
i:=0
for{
//执行若干次后退出
if i>5 {
break
}
i++
fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
//从打点器中获取通道
<-ticker.C
}
//清理计时器
ticker.Stop()
例如:
//创建计时器,每隔1秒后定时器会向channel发送一个事件,包含当前时间。
ticker := time.NewTicker(time.Second * 1)
i:=0
for{
//执行若干次后退出
if i>5 {
//清理计时器
ticker.Stop()
//跳出循环
break
}
//从打点器中获取通道
select{
case <-ticker.C:
i++
fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
}
}
例如:
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for{
select{
case t := <-ticker.C:
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
}
}
time.Tick
time.Tick()
返回的是一个通道,每隔指定时间就会有数据从通道中出来。
func Tick(d Duration) <-chan Time {
if d <= 0 {
return nil
}
return NewTicker(d).C
}
实现固定时间重复执行,即定期循环。
for{
time.Sleep(time.Second * 2)
fmt.Println(time.Now())
}
更优雅的方案
-
range
是可以不接收遍历的值的,若不接收则可简写。
for range time.Tick(time.Second * 2){
fmt.Println(time.Now())
}
ticker.Stop
func (t *Ticker) Stop() {
stopTimer(&t.r)
}
-
ticker.Stop()
方法会关闭一个Ticker
,关闭后将不会再发送更多的tick信息。 -
ticker.Stop()
方法不会关闭通道t.C
,只能避免从通道的读取不正确的成功。
ticker.Reset
func (t *Ticker) Reset(d Duration) {
if t.r.f == nil {
panic("time: Reset called on uninitialized Ticker")
}
modTimer(&t.r, when(d), int64(d), t.r.f, t.r.arg, t.r.seq)
}
时间段
time.Duration
-
time.Duration
是time
包定义的一个类型,表示一段时间间隔。 -
Duration
类型表示两个时间点之间经过的时间段,以纳秒为单位,可表示的最长时间段大约290年。 -
Duration
时间段实际上是int64
类型,单位纳秒。
实际编程中不会直接赋值时间段为一个数字,而是会采用time
包提供的常量。
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)
时间运算
Add
- 获取时间点增加时间段后的时间点
func (t Time) Add(d Duration) Time
例如:计算1天后的时间点
t1 := time.Now()
fmt.Println(t1)//2021-04-18 18:27:30.482194 +0800 CST m=+0.002020201
t2 := t1.Add(time.Hour * 24)
fmt.Println(t2)//2021-04-19 18:27:30.482194 +0800 CST m=+86400.002020201
例如:计算1天前的时间点
t2 := t1.Add(-time.Hour * 24)
fmt.Println(t2)//2021-04-17 18:30:48.1375322 +0800 CST m=-86399.998035299
Sub
- 计算两个事件之间的差值
func (t Time) Sub(u Time) Duration
例如:
t1 := time.Now()
fmt.Println(t1)//2021-04-18 18:27:30.482194 +0800 CST m=+0.002020201
t2 := t1.Add(time.Hour * 24)
fmt.Println(t2)//2021-04-17 18:30:48.1375322 +0800 CST m=-86399.998035299
d := t2.Sub(t1)
fmt.Println(d)//24h0m0s
时间比较
Equal
func (t Time) Equal(u Time) bool
- 判断两个时间点
t
和u
是否相同,会考虑时区的影响。
例如:
t1 := time.Now()
fmt.Println(t1)//2021-04-18 18:27:30.482194 +0800 CST m=+0.002020201
t2 := t1.Add(time.Hour * 24)
fmt.Println(t2)//2021-04-17 18:30:48.1375322 +0800 CST m=-86399.998035299
flag := t2.Equal(t1)
fmt.Println(flag)//false
Before
func (t Time) Before(u Time) bool
- 判断时间点
t
是否在指定时间点u
之前
After
func (t Time) After(u Time) bool
- 判断某时间点
t
是否在指定时间点u
之后
网友评论