这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
GO日期与时间——time包
实际开发中,经常会遇到日期和时间相关的操作,比如格式化日期和时间,解析一个日期时间字符串等。而在Go 语言中通过标准库time 包处理日期和时间相关的问题。
1、Time类型
Time
代表一个纳秒精度的时间点。程序中应使用Tim
e 类型值来保存和传递时间,而不是指针。就是说,表示时间的变量和字段,应为time.Time
类型,而不是*time.Time
类型。一个Time
类型值可以被多个 go 协程同时使用。- 时间点可以使用
Before、After 和 Equal
方法进行比较。 Sub
方法让两个时间点相减,生成一个Duration
类型值(代表时间段)。Add
方法给一个时间点加上一个时间段,生成一个新的 Time 类型时间点。Time
零值代表时间点January 1, year 1, 00:00:00.000000000 UTC
。因为本时间点一般不会出现在使用中,IsZero
方法提供了检验时间是否是显式初始化的一个简单途径。- 每一个
Time
都具有一个地点信息(即对应地点的时区信息),当计算时间的表示格式时,如Format、Hour 和 Year
等方法,都会考虑该信息。 Local、UTC 和 In
方法返回一个指定时区(但指向同一时间点)的Time
。修改地点 / 时区信息只是会改变其表示;不会修改被表示的时间点,因此也不会影响其计算。- 通过
==
比较Time
时,Location
信息也会参与比较,因此Time
不应该作为map
的key
。
Time 的内部结构:
type Time struct {
// sec gives the number of seconds elapsed since
// January 1, year 1 00:00:00 UTC.
sec int64
// nsec specifies a non-negative nanosecond
// offset within the second named by Seconds.
// It must be in the range [0, 999999999].
nsec int32
// loc specifies the Location that should be used to
// determine the minute, hour, month, day, and year
// that correspond to this Time.
// Only the zero Time has a nil Location.
// In that case it is interpreted to mean UTC.
loc *Location
}
常用函数或方法:
1. 零值的判断
Time 的零值是 sec
和 nsec
都是 0,表示 1 年 1 月 1 日。Time.IsZero()
函数用于判断 Time 表示的时间是否是 0 值。
2. 与 Unix 时间戳的转换
time.Unix(sec, nsec int64)
通过 Unix 时间戳生成time.Time
实例;time.Time.Unix()
得到 Unix 时间戳;time.Time.UnixNano()
得到 Unix 时间戳的纳秒表示;
3. 格式化和解析(实际开发中常用到)
time.Parse
time.ParseInLocation
time.Time.Format
4. 实现序列化或反序列化相关接口
Time
实现了:
encoding
包中的BinaryMarshaler 、 BinaryUnmarshaler 、 TextMarshaler 和TextUnmarshaler
接口;encoding/json
包中的Marshaler 和 Unmarshaler
接口。gob
包中的GobEncoder 和 GobDecoder
接口。- 对于文本序列化 / 反序列化,通过
Parse 和 Format
实现; - 对于二进制序列化,需要单独实现。
6. Round 和 Truncate 方法
获取当前时间整点的 Time
实例。例如,当前时间是 15:54:23
,需要的是 15:00:00
,time 包也给我们提供了专门的方法,功能强大且性能好,这就是 Round
和 Trunate
,它们区别,一个是取最接近的,一个是向下取整。
使用示例:
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)
2、定时器
定时器是进程规划自己在未来某一时刻接获通知的一种机制。两种常用定时器: Timer
(到达指定时间触发且只触发一次)和 Ticker
(间隔特定时间触发)。
2.1、Timer 相关函数或方法的使用
通过time.After
模拟超时:
c := make(chan int)
go func() {
// time.Sleep(1 * time.Second)
time.Sleep(3 * time.Second)
<-c
}()
select {
case c <- 1:
fmt.Println("channel...")
case <-time.After(2 * time.Second):
close(c)
fmt.Println("timeout...")
}
time.Stop 停止定时器 或 time.Reset 重置定时器:
start := time.Now()
timer := time.AfterFunc(2*time.Second, func() {
fmt.Println("after func callback, elaspe:", time.Now().Sub(start))
})
time.Sleep(1 * time.Second)
// time.Sleep(3*time.Second)
// Reset 在 Timer 还未触发时返回 true;触发了或 Stop 了,返回 false
if timer.Reset(3 * time.Second) {
fmt.Println("timer has not trigger!")
} else {
fmt.Println("timer had expired or stop!")
}
time.Sleep(10 * time.Second)
// output:
// timer has not trigger!
// after func callback, elaspe: 4.00026461s
- 如果定时器还未触发,
Stop
会将其移除,并返回true
;否则返回false
;后续再对该Timer
调用Stop
,直接返回false
。 Reset
会先调用stopTimer
再调用startTimer
,类似于废弃之前的定时器,重新启动一个定时器。返回值和Stop
一样。Sleep
的是通过Timer
实现的,把当前goroutine
作为arg
参数(getg()
)。
2.2、Ticker 相关函数或方法的使用:
Ticker
和Timer
类似,区别是:Ticker
中的runtimeTimer
字段的period
字段会赋值为NewTicker(d Duration)
中的 d ,表示每间隔 d 纳秒,定时器就会触发一次。- 除非程序终止前定时器一直需要触发,否则,不需要时应该调用
Ticker.Stop
来释放相关资源。 - 如果程序终止前需要定时器一直触发,可以使用更简单方便的
time.Tick
函数,因为Ticker
实例隐藏起来了,因此,该函数启动的定时器无法停止
3、时区
不同国家(有时甚至是同一个国家内的不同地区)使用不同的时区。对于要输入和输出时间的程序来说,必须对系统所处的时区加以考虑。Go 语言使用 Location
来表示地区相关的时区,一个 Location
可能表示多个时区。
time
包提供了Location
的两个实例: Local
和 UTC
。Local
代表当前系统本地时区; UTC
代表通用协调时间,也就是零时区。 time 包默认(为显示提供时区)使用 UTC
时区。
获得特定时区的实例:
函数 LoadLocation 可以根据名称获取特定时区的实例。函数声明如下:
func LoadLocation(name string) (*Location, error)
如果 name 是 “” 或 “UTC”
,返回 UTC
;如果 name 是 “Local”
,返回 Local
;否则 name 应该是 IANA 时区数据库里有记录的地点名(该数据库记录了地点和对应的时区),如 “America/New_York”。
4、time总结:
time
包提供了时间的显示和计量用的功能。日历的计算采用的是公历
1. Location
:代表一个地区,并表示该地区所在的时区(可能多个)。 Location 通常代表地理位置的偏移,比如CEST 和 CET
表示中欧。
Time
:代表一个纳秒精度的时间点,是公历时间。
3. Duration
:代表两个时间点之间经过的时间,以纳秒为单位。可表示的最长时间段大约 290 年,也就是说如果两个时间点相差超过 290年,会返回 290 年,也就是 minDuration(-1 << 63) 或 maxDuration(1 << 63 - 1)
。类型定义: type Duration int64
。
将 Duration 类型直接输出时,因为实现了 fmt.Stringer
接口,会输出人类友好的可读形式,如:72h3m0.5s
。
Timer 和 Ticker
:定时器相关类型。
5. Weekday 和 Month
:这两个类型的原始类型都是 int,定义它们,语义更明确,同时,实现 fmt.Stringer
接口,方便输出