Go 标准库 time 包简介
最近打算开始对 Go 语言标准库的每个包和每个方法进行讲解和添加一些使用实例。
这个想法也是由来很久了,但一直没有行动起来,今天,就把它捡起来吧。
Go 语言标准库的 time 包提供了 测量 和 显示 时间的功能,外加一个定时器
而且呢,time 包里的所有函数都有一个前提假设:日历计算总是假设一个公历 ( 格里高利历 ),没有闰秒
格里高利历
对的,你没看错,是 「 格里高利历 」,而不是格里高日历。格里高利历是公历的标准名称,教皇格里高利十三世于 1582 年颁布。而公元即 「 公历纪元 」,又称 「 西元 」,而我国也在开国大典前几天,也就是 1949 年 9 月 27 日确定使用公历 ( 不然没法确定开国时间 )
格里高利历的最大特点,就是历年平均长度为 365 日 5 时 49 分 12 秒,比回归年长 26 秒,没隔 3000 年左右仍存在1天的误差
闰秒
「 闰秒 」 就是 1 分钟有 61 秒, 「 跳秒 」 都安排在 6 月 30 日,或是 12 月 31 日的最后一瞬间。
总所周知,1 分钟只有 60 秒,那为什么会多出 1 秒呢 ?
因为地球的自转转速并不是均匀的,有时快有时慢。所以呢,为了配合这种时慢时快的自转。每当地球自转变化引起的时间误差积累到与原子钟相关接近 1 秒时,就要人为地把时钟增加或减少 1 秒,从而使两者重新协调一致。这增加或减少的1秒称为 「 跳秒 」。若是增加的,就是 「 正跳秒 」( 拨慢 1 秒);若是减少的就是 「 负跳秒 」( 拨快 1 秒 )
Go 语言 time 包的两种时钟
其实,Linux 世界里有四种时钟 ( clock )
CLOCK_REALTIME CLOCK_MONOTONIC CLOCK_MONOTONIC_RAW CLOCK_BOOTTIME
-
CLOCK_REALTIME
,就是非常出名的 「 wall time 」,即是实际的时间。其实就是我们经常看到的,20xx 年 xx 月 xx 日 xx 时 xx 分 xx.xx 秒,只不过保存的时候使用的是时间戳格式
wall time,挂钟,很古老很古老的欧洲时代,家家户户都有一个挂钟,用来看时间的,其实,就相当于现在大家在用的手表
CLOCK_REALTIME
时间是可以设置的,用户可以使用命令 date 或是系统调用去修改。如果使用了 NTP, 也会被 NTP 修改。
当系统休眠 ( suspend ) 时,仍然会运行的 ( 原理就是,系统恢复时,kernel 去作补偿 )
-
CLOCK_MONTONIC
,即单调时间,即从某个时间点开始到现在过去的时间。用户不能修改这个时间,但是当系统进入休眠 ( suspend ) 时,
CLOCK_MONOTONIC
是不会增加的CLOCK_MONTONIC
其实就是一个计时器,当电脑开机的时候重新开始计时,当电脑睡眠的时候暂停计时,当电脑关机的时候停止计时只不过这个计时器采用的不是 秒为单位,或者通常计时器上的
xx.xx
,而是采用的 CPU 时钟 -
CLOCK_MONOTONIC_RAW
,和CLOCK_MONOTONIC
类似,不同之处是MONOTONIC_RAW
不会受到NTP
的影响CLOCK_MONOTONIC
会受到NTP
的影响并不是说NTP
会去修改CLOCK_MONOTONIC
,使其不连续而是说当
NTP server
和本地的时钟硬件之间有问题,NTP 会影响到CLOCK_MONOTONIC
的频率,但是MONOTONIC_RAW
则不会受其影响 -
CLOCK_BOOTTIME
,与CLOCK_MONOTONIC
类似,但是当休眠 (suspend
) 时,会依然增加
了解了 Linux 世界的四个时钟后,我们就来说说 Go 语言里的两个时钟
Monotonic Clocks
Go 语言的 time 包主要使用了两个时钟
CLOCK_REALTIME
,就是非常出名的 「 wall time 」,即是实际的时间CLOCK_MONTONIC
,即单调时间,即从某个时间点开始到现在过去的时间
操作系统提供 「 wall time 」,其可以根据时钟同步而变化,以及 「 monotonic clock 」,不会根据时钟同步而变化。
两者的一般规则是 「 wall time 」 用于告知时间,而 「 monotonic clock 」 用于测量时间
Go 语言为了简化 API,并没有为两种时钟准备两套 API,而是在 time.Now()
方法返回的 Time
结构中同时包含了 wall clock 读数和 monotonic clock 读数;
而之后对 Time
结构的操作,显示时间相关的操作使用的是 wall time ,测量和计算时间的操作使用的是 monotonic clock ,特别是比较和减法,使用 monotonic clock 读数。
例如,下面的代码,即使在定时操作期间更改 wall time ,此代码也始终计算大约 20 毫秒的时间间隔
start := time.Now() ... operation that takes 20 milliseconds ... t := time.Now() elapsed := t.Sub(start)
而另外一些方法,比如 time.Since(start)
,time.Until(deadline)
和 time.Now().before(deadline)
等,同样不受 wall time 时间重置的影响
结束语
有关 time 包两个时间的更多细节,我们会再下一章节继续阐述,这章节就到这里吧