程序中的时间问题总结

日期和时间在程序中应用广泛,但是,一旦涉及到跨时区的日期和时间的处理时,大多数人总还是不明白如何正确地处理日期和时间,今天总结一下。

image-20181211171522136

常用概念

GMT和UT时间

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

UT(世界时)和GMT(格林威治标准时)是相同的时间标准,都是基于天体观察的。UT 是相同标准的“科学”名称。GMT:格林威治时间,是标准的“民间”名称。因此,格林威治标准时(GMT)和世界时 (UT) 是相等的。

由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治平时基于天文观测本身的缺陷,已经被原子钟报时的协调世界时(UTC)所取代。

UTC时间

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

对于大多数用途来说,UTC时间被认为能与GMT时间互换,但GMT时间已不再被科学界所确定。

时区

各个国家分布在地球的不同位置上,因此不同国家的日出、正午、日落时间可能有所偏差,在现实生活中,人们更关注本地时间(Localtime),因为和 UTC 相比,本地时间更适合用于当地的生产和生活。1863 年,时区的概念被首次提出,它以区域为范围设定标准时间,某地的本地时间通常指该地所在时区的标准时间。理论时区以被 15 整除的子午线为中心,向东西两侧延伸 7.5 度,即每 15 度划分一个时区,这是理论时区。但是,为了避开国界线,有的时区的形状并不规则,而且比较大的国家以国家内部行政分界线为时区界线,这是实际时区,即法定时区。

CST时间

北京时间,又名中国标准时间,是中国大陆的标准时间,从时区上来讲,北京位于东八区,故其时区为 UTC+8(或GMT +8),北京时间比格林威治标准时间早 8 小时,即比协调世界时快八小时(也即UTC+8),与香港、澳门、台北、吉隆坡、新加坡等地的标准时间相同。

时区表示法

参考ISO_8601日期格式标准ISO_8601日期格式标准

  • 如果时间在零时区,并恰好与协调世界时相同(UTC时间也叫祖鲁时间,北约音标字母中用“Zulu”表示“Z”),那么(不加空格地)在时间最后加一个大写字母Z。Z是相对协调世界时时间0偏移的代号,如下午2点30分5秒表示为14:30:05Z143005Z;只表示小时和分,为1430Z或14:30Z;只表示小时,则为14Z或14Z。
  • 其他时区用实际时间加时差表示,当时的UTC+8时间表示为22:30:05+08:00223005+0800,也可以简化成223005+08
  • 合并表示时,要在时间前面加一大写字母T,如要表示北京时间2004年5月3日下午5点30分8秒,可以写成2004-05-03T17:30:08+08:0020040503T173008+08

更多(不需要掌握)

CST:实际上,CST可以同时代表以下 4 个不同的时区,不过我们使用默认为中国标准时间。

  • Central Standard Time (USA) UT-6:00
  • Central Standard Time (Australia) UT+9:30
  • China Standard Time UT+8:00
  • Cuba Standard Time UT-4:00

闰秒:是指为保持协调世界时(UTC)接近于世界时时刻(UT),由国际计量局统一规定在年底或年中(也可能在季末)对协调世界时增加或减少1的调整,操作系统会进行处理,对程序员应该影响不大

DST夏令时(Summer Time) 又称日光节约时制或日光节约时间,是一种为节约能源而人为规定地方时间的制度。一般在天亮较早的夏季人为将时间调快一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏时制的国家具体规定不同,目前全世界有近 110 个国家每年要实行夏令时,如美国、欧盟等;中国、日本等国家曾经实行过夏令时,但是目前不予实行。

程序员用到的时间

操作系统

Unix 时间

Unix 时间又称 Posix 时间,UNIX时间戳是程序中最常用的,指自 UTC 时间 1970 年 1 月 1 日 0 时 0 分 0 秒起值现在的总秒数。是 Unix 和 类 Unix 系统使用的表达方式,他的特点是和时区无关,无论在地球上的那个角落,同一时刻,UNIX时间戳都是一样的。所以可以作为一个通用的时间偏移度量,计算每个时区当地时间时,都可以用时间戳推算出来。,可用 date +%s 查看 Unix 时间:

image-20181211180804328

由上可知,Unix 时间 1479655113 表示的日期为 Sun Nov 20 15:18:33 UTC 2016。

现在有些系统采用 32 位有符号整数保存 Unix 时间,所以它最多只能表示 136 年,即:1901-12-13 20:45:52 至 2038-1-19 3:14:07。和 千年虫问题 类似,32 位 Unix/Linux 的系统在 2038 年可能会发生故障,所以又叫2038年问题

时区信息数据库

我们经常会看到这样的时区表示:”Asia/Shanghai”,这些信息就是保存在时区信息数据库里面的。Ubuntu安装命令如下:

1
export DEBIAN_FRONTEND=noninteractive(非交互安装) && apt-get install -y tzdata

硬件时钟

硬件时钟也叫RTC(Real Time Clock)或者CMOS时钟,这个是保存在BIOS中的,由主板电池供电来维持运行,系统开机时要读取这个时间,并根据它来设定系统时间。仅能保存:年、月、日、时、分、秒这些时间数值,无法保存当前时区以及是否使用夏令时调节。所以BIOS时间即硬件时间没有时区。

系统时钟

系统时钟也叫软件时钟,在系统时钟里是有时区等概念的,在Linux内核里,是保存为自 UTC 时间 1970 年1月1日经过的秒数。系统启动时会读取硬件时钟,并根据/etc/adjtime的设置计算当前的时钟。系统启动之后,系统时钟与硬件时钟独立运行,Linux 通过时钟中断计数维护系统时钟。一般说来就是我们执行 date 命令看到的时间,linux系统下所有的时间调用(除了直接访问硬件时间的命令)都是使用的这个时间。

/etc/localtime

这个文件一般情况下是一个软链接,链接到/usr/share/zoneinfo/目录下的一个对应时区的二进制文件,比如设置Asia/Shanghai的时区,则/etc/localtime -> /usr/share/zoneinfo/Asia/Shanghai,调用date等工具获取时间时会考虑这个配置。建议并强烈建议将这个文件设置为软链接,很多人会直接拷贝文件,其实是不推荐的。所有的Linux系统都会依赖这个文件。

1
ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

/etc/timezone

这个文件一般会记录时区的直接文字表示,或者是一个时间偏移(很少见),比如如果设置时区为Asia/Shanghai,则这个文件的内容就会是Asia/Shanghai。这个文件并不是在所有Linux中都存在。这个文件一般也仅仅是一个简单的表示(所以好像改了localtime就行)。

tip:What’s the difference between localtime and timezone files?

程序和数据库中

基于“数据的存储和显示相分离”的设计原则,我们只要把表示绝对时间的时间戳(无论是Long型还是Float)存入数据库,在显示的时候根据用户设置的时区格式化为正确的字符串即可。想了解更多可以看下廖雪峰的这篇文章