各位小伙伴们,大家好,本文是农历辛丑年本公众号所发表的第一篇文章。时光如梭,短短的一年又过去了,不禁时时感叹时间之匆匆。就好像朱自清说的“我的日子滴在时间的流里,没有声音,也没有影子”。既然说到了时间这个话题,本文就借此来详细地聊一下时间。本文中接下来的内容可能会涉及到天文,地理,历史,计算机等方面的知识。但不会涉及到相对论等宏观的宇宙学原理。
1 历法与时间
世界上主流的历法根据参照物的不同可以分为阴历和阳历。所谓阴历是以月亮作为计时参照物的历法,阳历是以太阳作为计时参照物的历法。在人类过去的几千年发展历程中,有各种各样标准的阴历和阳历,这些历法的计时准确度各不相同。当然,中国古代的历法严格意义上来说应该被称为农历而不是阴历,因为农历不仅有着以阴历为基础的计时系统,还有着以“二十四节气”为代表的阳历计时系统。
近代以来,随着西方工业革命后欧洲的崛起,它的历法“格里高利历”(1582年颁行)也被全世界绝大多数国家所使用,这个“格里高利历”即为我们俗称的“公历”。公元,是纪年法称谓,为纪年体系。该纪年法是以耶稣诞生为公元元年,公元元年之前的称公元前。所谓的2020年就是从公元元年开始算起的两千零二十年。在公历纪年系统中,规定了一年有365天(平年)每4年产生一个闰年(366天)。因此,普通年份能被4整除,且不能被100整除的,是闰年(如2004年就是闰年),世纪年份能被400整除的是闰年(如2000年是闰年,1900年不是闰年)。
以上是对纪年法的简述,仔细回想一下,我们精确地表述某一时刻的时候,一般表述为:XXXX年XX月XX日XX时XX分XX秒。由此可以看出,一个精准的时刻是由“年月日时分秒”这六个部分组成的,而这六个部分中,可以大体分成两类,一类是由“年月日”组成的日期,第二类是由“时分秒”组成的时间。第一类是与我们之前讲述的纪年法相关,第二类则完完全全是由时间所组成。
在现代历法中,一天由24个小时组成,这个大家都知道。但是,由于我们地球是一个球体,,因此导致了地球是自西向东自转,东边比西边先看到太阳,东边的时间也比西边的早。东边时刻与西边时刻的差值要以时计,而且还要以分和秒来计算,这给人们带来不便。
为了克服时间上的混乱,1884年在华盛顿召开的一次国际经度会议(又称国际子午线会议)上,规定将全球划分为24个时区(东、西各12个时区)。规定英国(格林尼治天文台旧址)为中时区(零时区)、东1—12区,西1—12区。每个时区横跨经度15度,时间正好是1小时。最后的东、西第12区各跨经度7.5度,以东、西经180度为界。每个时区的中央经线上的时间就是这个时区内统一采用的时间,称为区时,相邻两个时区的时间相差1小时。如图1所示。
图1 世界时区图
2 GMT和UTC和UNIX时间戳
GMT,即格林尼治标准时间,也就是世界时。GMT的正午是指当太阳横穿格林尼治子午线(本初子午线)时的时间。但由于地球自转不均匀不规则,导致GMT不精确,现在已经不再作为世界标准时间使用。
UTC,即协调世界时。UTC是以原子时秒长为基础,在时刻上尽量接近于GMT的一种时间计量系统。为确保UTC与GMT相差不会超过0.9秒,在有需要的情况下会在UTC内加上正或负闰秒。UTC现在作为世界标准时间使用。
所以,UTC与GMT基本上等同,误差不超过0.9秒。
计算机中的UNIX时间戳,是以GMT/UTC时间(1970-01-01T00:00:00)为起点,到具体时间的秒数,不考虑闰秒。这么做当然是为了简化计算机对时间操作的复杂度。
比如我的电脑现在的系统时间为2021年3月5日21点23分0秒,因为我的电脑默认时区为东8区,则0时区的时间为2021年3月5日13点23分0秒,则UNIX时间戳为1614950580秒。
有了上面准备知识之后,有一个问题需要大家思考,计算机(RTC芯片或者RTC模块)到底是如何来存储时间的?很显然,有了上述的铺垫之后,我们可以知道,所有的RTC模块或者RTC芯片其内部只不过是一个计数器,准确地说应该是(秒)计数器,它在工作时,受秒晶振触发,每一秒计数一次。
当我们需要读取时间的时候,就用软件或者硬件的手段将UNIX时间戳转换成UTC时间,然后根据计算机的时区,将其修正为当前时区的时间。
当我们需要设置时间的时候,芯片使用软件或者硬件手段将需要设置的当前时区的时间先转成UTC时间,接着将UTC时间转换成UNIX时间戳,最后将其写入RTC模块或者芯片的计数器存储器里面。
3 为何我们还需要研究UNIX时间戳转换成UTC时间的代码?
既然我们现在有了众多的RTC模块,为什么还要来研究UNIX时间戳如何转换成UTC时间呢?这个话题的开展,是源于STM32F103系列单片机的使用。众所周知,STM32单片机全系拥有RTC功能,但是对于STM32F103系列的单片机,它的RTC时钟非常简单,归根到底,它就是一个32位数据长度的非易失性秒计数器(VBAT引脚保证不掉电)。它并没有任何功能模块可以将这个计数器里面的时间转换成UTC时间,你能得到的仅仅是一个32位的寄存器数值,这个RTC寄存器会在你设置的初始计数值的前提下,每1秒进行自加,其内部简化结构如图2所示。
图2 STM32F103 内部RTC简化结构图
因此,我们如果想要使用这个RTC来进行时间计数时,最简单标准的做法如下:
(1)当用户需要校准时间时,先将用户设置的当地时区时间转换成UTC时间,然后将UTC时间转换成UNIX时间戳写入RTC计数器,RTC计数器更新完成初始时间后,进行秒自加;
(2)当用户程序需要读取时间时,先将当前RTC计数器中的UNIX时间戳读出,然后将其转换成UNIX时间戳转换成UTC时间,最后,将UTC时间转换成当地时间进行显示。
具体步骤如图3所示。
图3 读取STM32F103中RTC的步骤
接下来的文章中,我们将会详细讨论UNIX时间戳和UTC时间的转换算法。