如何在计算机中表示时间?(上)


0、前言

笔者没有很深入地了解过各种时间标准。要精确的表示一个时间是真的挺复杂的,其中涉及到的知识太多,笔者做不到完全掌握,也没有必要。

本文尽量讲的通俗易懂,但定义可能就不精准,所以请以怀疑的眼光看待此文。如果有错误的地方,也恳请指出。

1、时间的定义

首先,回想一下,国际单位制中时间的基本单位是什么?答:秒。

那么,“秒”的定义是什么?一般来说,我们说1天对应24小时,1小时对应60分钟,1分钟对应60秒。而”天“指的就是地球自转一周的时间。

所以1秒就是1/(24*60*60)=1/86400天,就是地球自转一周时间的1/86,400。


但是,地球自转一周的时间是变化的(貌似地球自转在加快,自转一周的时间越来越短)。如果采用原来的定义,那么1秒和1秒的时间长度可能是不同的。这对于某些对时间要求很苛刻的工作来说是不行的。

原子时

目前国际上对秒的定义是:

秒,符号为s,国际单位制时间单位。其定义是,将铯-133原子不受扰动的基态超精细能级跃迁频率ΔνCs的值固定为9192631770赫兹,赫兹等于s−1。

看不懂吗?我也看不懂。为了方便起见,下文中就称它为”原子秒“吧。(”原子秒“这个称呼我不确定是否标准,但你get到意思就行)


当然,肯定没有绝对精准的东西,不过嘛:

1999年12月29日NIST-F1 铯原子钟其不确定度为1.5×10−15,相当于2000万年不差一秒。

2020年10月27日,国际计量局(BIPM)报导,德国ptb-csf2铯原子钟,在1亿8千7百万年的时间内的误差不会超过1秒钟(1.7 × 10−16) 。法国syrte-fo2铯原子钟,在1亿4千4百万年的时间内的误差不会超过1秒钟(2.2 × 10−16)。 俄罗斯su-csfo2铯原子钟,在1亿4千4百万年的时间内的误差不会超过1秒钟(2.2 × 10−16)。

看这些数据就知道误差有多小了,是真的可以忽略不计。所以,我们就简单的认为两个原子秒的时间长度就是完全一样的好了。


那么既然有了原子秒,也就有了原子时

国际原子时(英语:International Atomic Time、法语:Temps Atomique International, TAI[1]

其起点为世界时1958年的开始。

貌似国际原子时的简称一般是TAI而不是IAT,不过这个不重要。

总之我们就简单的认为原子时就是从1958年1月1日0时0分0秒(UT)以来经过的原子秒数。(这里的UT是世界时,就是以英国格林尼治那旮沓为准)


到这里我不免畅想一下,如果大家都用国际原子时那该有多好啊……

当然,那是不可能的。如果你问我现在是什么时间?我告诉你XXXXXXXXX秒,那你一定会想打死我。

正常表示法

接下来我们仍然要回到正常的时间表示法,也就是采用年、月、日、时、分、秒的表示法。


建议阅读下面这篇文章。

到底是 GMT+8 還是 UTC+8 ?

格林尼治平均时间(英语:Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台当地的平太阳时,因为本初子午线被定义为通过那里的经线

当我查GMT的时候,出现了“平太阳时”,当我查“平太阳时”的时候,又出现了各种奇奇怪怪的名词,干脆我就不管了。

简单地说,GMT就是以格林尼治那旮沓为准的时间,也就是我们目前的时间标准。

尽管GMT也想了各种办法让自己的时间尽量精确,但如前所述,地球自转时间在变,这就导致GMT不可能绝对精准。


所以接下来就出现了UTC。

协调世界时(英语:Coordinated Universal Time,法语:Temps Universel Coordonné,简称UTC)是最主要的世界时间标准,其以原子时秒长为基础,在时刻上尽量接近于**格林威治标准时间**。

UTC采用了原子秒,这样就很精准了。UTC也把时间分为天、小时、分钟和秒。

但是,出现了问题,如果我们很简单的这样做了。那么由于原子秒和普通秒的差,不断累计下去,在未来,一个人一看手表显示14:00,却处在黑夜中的情况,这样就不利于人们交流了。

所以,UTC中还有一个“闰秒”的机制,让它来尽量接近GMT时间。

协调世界时把时间分为天、小时、分钟和秒。通常,天是使用格里历(公历)定义的,但也能使用儒略日。每天包含24小时,每小时包含60分钟。一分钟通常有60秒,但加入了随机的闰秒后,一分钟可能是61秒或59秒[12]。因此,在UTC系统的时间尺度中,秒和比秒小的单位(毫秒、微秒等)其长度是固定的,但是对于分钟和比分还大的单位(小时、天、周等),其长度是可变的。国际地球自转服务组织(IERS)做出插入闰秒的决定,并至少在加入前6个月发布在该组织的“公告C”中[13][14] 。闰秒是无法提前很早预知的,因为地球的自转速率是不可预测的[15]

UTC时间中“1秒”就是1原子秒,但1分钟可能有61原子秒或59原子秒。闰秒是全世界大家一起加或减的,所以UTC时间仍然不会出现偏差。

不管怎样,“闰秒”的出现保证了保证协调世界时(UTC)与世界时(UT1)相差不超过0.9秒[9]

(麻烦把UT1视作GMT就好,不要再管这里面的差别了)

总结

UTC采用原子秒,更加准确。但如果你是一个普通人,真的就不用在意这些细节,因为此时的UTC和GMT相差不会超过0.9秒,基本可以认为是一样的。

当然,如果你从事的行业对时间要求很苛刻,比如科学、金融、军事等领域。那么请忘掉我上面说的所有东西(因为我可能已经误导了你),请专业人士对你进行培训。

2、时间与计算机

在计算机上我们肯定也要跟时间打交道,如何处理、存储时间类型的数据?接下来介绍这方面的内容。

建议

那么,首先我要告诉你一件非常重要的事:

如果大概了解了UTC、GMT的区别,知道了闰秒的概念。那么,请忘记它。

请把GMT视作UTC,把1天就看做86400秒,把每天和每秒的长度看作是等长的。


为什么?

对于普通程序员

因为几乎所有现代操作系统都假设在所有情况下1天= 24×60×60 = 86400秒。如Windows,Linux,Apple,Android都对闰秒无知。

而且就算要设计,也非常麻烦,因为什么时候出现闰秒,取决于地球自身转速的变化,是不能预测的。所以干脆就不设计了。

所以如果你的程序,是建立在一个不准确的操作系统之上的,那么你也不可能得到一个准确的时间值。


简单地说,如果你是普通应用型程序员,就别考虑那么多了。


说实在的,个人计算机的时间本来就不怎么准确,时间的变动本来就有点随意,所以影响不是很大。


如果你问我,那为什么感觉我的计算机时间很准呢?最主要是,为什么我的多台PC之间、PC与手机、手机与iPad之间的时间基本都是同步的呢?

这是通过NTP(Network Time Protocol)来达到的,简单的说就是时不时地用网络进行对时,所以对人来说,感觉上大概就是准的。

如果你打开电脑的时间设置可以看到:

image-20210401141020554

电脑是可以自动调节时间甚至是时区的。

而手机上也有:

6b5512a9-aa8d-44e9-b370-9ae52078cccf

这里我真的试了一下,关闭了NTP,把电脑的时间调到1个月前。电脑还是正常运行,网页也能正常浏览。并没有什么问题。

image-20210301141401660

我们再用程序测试一下:

1
2
3
4
5
6
7
8
9
10
import java.util.Date;

class Test {
public static void main(String[] args) throws Exception {
while (true) {
Thread.sleep(1000);
System.out.println(new Date().getTime());
}
}
}
0f74465a2629a077f0dbecc82e3a977

这里我将电脑的时间改成了1991年,然后又改了回来,所以你会看到数字变小又变大。

可以看到,以上用Java程序执行的代码中,因为依赖于系统时间,所以后执行的代码得到的时间反而变小了。


这就说明系统时间确实有一定的不可靠性。用看到的一句话总结:

程序本来就应该认为本机的时间是不准的。

对于特殊程序员

虽说一般程序员可能不用考虑这么多问题,对于某些特殊的程序员还是比较麻烦的。

如果你设计的程序跑在一台连接着原子钟的计算机上,作为NTP的上游,那么我不知道你要做什么。

但如果你的程序跑在NTP的下游,比如说,你就是一个系统程序员,那我大概知道你要做什么。


当要增加正闰秒时,这一秒是增加在第二天的00:00:00之前,效果是延缓UTC第二天的开始。当天23:59:59的下一秒被记为23:59:60,然后才是第二天的00:00:00。如果是负闰秒的话,23:59:58的下一秒就是第二天的00:00:00了,但目前还没有负闰秒调整的需求。

让我们回顾一下,闰秒是为了协调UTC和GMT时间的,它可以加1秒(正闰秒)也可以减1秒(负闰秒,没发生过)。

再看一下NTP:

NTP意图将所有参与计算机的协调世界时(UTC)时间同步到几毫秒的误差内。

可以看到,NTP是以UTC时间为准的。我们不考虑NTP上游怎么处理的,只考虑下游。

事实上,下游的计算机处理不好时,就会出现“闰秒危机”:

闰秒危机的问题是当NTP传入23:59:60这个时间,秒的位置出现了60。而下游计算机看到了这个60,无法理解,不知道怎么处理,可能就会发生异常。

我们可以看到,闰秒危机的原因是下游计算机系统无法理解这个60。

(“负闰秒”应该不会出现闰秒危机,因为只是从23:59:58直接到00:00:00,下游计算机完全可以把这看做自己的表走慢了,然后纠正就好。)


所以如果你是一个系统程序员,那么对你来说最重要的就是在处理NTP接收到的时间时考虑60这个秒数,认为它是合法的,只要不触发异常,不崩溃就行,自己消化掉,不影响上层应用。仍然表现出1天86400秒的样子。


那么如果我现在用的操作系统就是不能处理60这个数字怎么办,也没办法迁移软件怎么办?

其实这应该是运维的事了:

  1. 在闰秒出现前关闭NTP,结束后重新开启NTP。
  2. Well,解决不了问题,就解决提出问题的人:从上游解决。

下面说说2:

阿里云ECS云服务器采用的方案是,将多出来的这一秒分平均分配到24小时(即86400秒)中,在闰秒时刻前12小时开始,闰秒后12小时结束。

参考链接