计算机中的时间
看完这篇文章相信你会对计算机中的时间有更系统全面的认识。
我经常自嘲,自己写的程序运行不超过3年,因为大部分项目方就早早跑路了。大多数项目上线后,你跟这个项目就再无瓜葛,关于时间你只需要保证时区正确就不会有太大问题,哈哈。 但是今天我想认真对待时间这个问题,作为一个库作者或基础软件作者,就需要考虑下游项目万一因为你处理时间不当而造成困扰,影响范围就比较广了。
计算机中与时间有关的关键词:
时间类型
时间戳(timestamp)
定时器(例如js中setInterval())
时间计算
时间段
超时(setTimeout())
时间片
GMT
UTC
Unix时间戳
ISO8601
CST
EST
看到这些你可能会疑惑,为何一个时间竟然如此复杂!!
如果下面的问题你都能答上来,那这篇文章对你的帮助微乎其微,不如做些更有意义的事情。
- 常用的时间格式,他们都遵循哪些标准?
- 什么是GMT?
- 什么是UTC?
- GMT UTC 和ISO8601有什么区别?
- RFC5322是什么?
- RFC5322 采用的是GMT还是UTC?
- ISO8601 使用的是UTC还是GMT?
- 在ISO8601中 Z可以使用+00:00表示吗?
- UTC什么时候校准?
- CST是东八区吗?
- Z是ISO 8601规定的吗,为什么是Z?
- 时区划分是哪个标准定义的?
- 为什么是1970年1月1日呢?
- 到了2038年时间戳溢出了怎么办?
- 计算机中时间的本质是一个long类型吗?
- WEB前后端用哪个格式传输好?
- '2024-01-01T24:00:00' 等于 '2024-01-02T00:00:00' ??
如果看文章太累,也可以B站搜 程序饲养员 看《计算机中的时间》那期视频。
正文开始
1. 两种时间标准
UTC和GMT都是时间标准,定义事件的精度。它们只表示 零时区 的时间,本地时间则需要与 时区 或偏移 结合后表示。这两个标准之间差距通常不会超过一秒。
UTC(协调世界时)
UTC,即协调世界时(Coordinated Universal Time),是一种基于原子钟的时间标准。它的校准是根据地球自转的变化而进行的,插入或删除闰秒的实际需求在短期内是难以预测的,因此这个决定通常是在需要校准的时候发布。 闰秒通常由国际电信联盟(ITU) 和国际度量衡局(BIPM) 等组织进行发布。由国际原子时(International Atomic Time,TAI) 通过闰秒 的调整来保持与地球自转的同步。
GMT(格林尼治标准时间)
以英国伦敦附近的格林尼治天文台(0度经线,本初子午线)的时间为基准。使用地球自转的平均速度来测量时间,是一种相对于太阳的平均时刻。尽管 GMT 仍然被广泛使用,但现代科学和国际标准更倾向于使用UTC。
2. 两种显示标准
上面我们讨论的时间标准主要保证的是时间的精度,时间显示标准指的是时间的字符串表示格式。我们熟知的有 RFC 5322 和 ISO 8601。
RFC 5322 电子邮件消息格式的规范
RFC 5322 的最新版本是在2008年10月在IETF发布的,你阅读时可能有了更新的版本。
RFC 5322 是一份由 Internet Engineering Task Force (IETF) 制定的标准,定义了 Internet 上的电子邮件消息的格式规范。该标准于2008年发布,是对之前的 RFC 2822 的更新和扩展。虽然 RFC 5322 主要关注电子邮件消息的格式,但其中的某些规范,比如日期时间格式,也被其他领域采纳,例如在 HTTP 协议中用作日期头部(Date Header)的表示。
格式通常如下:
Thu, 14 Dec 2023 05:36:56 GMT
时区部分为了可读可以如下表示:
Thu, 14 Dec 2023 05:36:56 CST
Thu, 14 Dec 2023 05:36:56 +0800
Thu, 14 Dec 2023 05:36:56 +0000
Thu, 14 Dec 2023 05:36:56 Z
但并不是所有程序都兼容这种时区格式,通常程序会忽略时区,在写程序时要做好测试。标准没有定义毫秒数如何显示。
需要注意的是,有时候我们会见到这种格式Tue Jan 19 2038 11:14:07 GMT+0800 (中国标准时间),这是js日期对象转字符串的格式,它与标准无关,千万不要混淆了。
ISO 8601
ISO 8601 最新版本是 ISO 8601:2019,发布日期为2019年11月15日,你阅读时可能有了更新的版本。
下面列举一些格式示例:
2004-05-03T17:30:08+08:00
2004-05-03T17:30:08+00:00
2004-05-03T17:30:08Z
2004-05-03T17:30:08.000+08:00
标准并没有定义小数位数,保险起见秒后面一般是3位小数用来表示毫秒数。 字母 "Z" 是 "zero"(零)的缩写,因此它被用来表示零时区,也可以使用+00:00,但Z更直观且简洁。
- 本标准提供两种方法来表示时间:一种是只有数字的基础格式;第二种是添加了分隔符的扩展格式,更易读。扩展格式使用连字符“-”来分隔日期,使用冒号“:”来分隔时间。比如2009年1月6日在扩展格式中可以写成"2009-01-06",在基本格式中可以简单地写成"20090106"而不会产生歧义。 若要表示前1年之前或9999年之后的年份,标准也允许有共识的双方扩展表达方式。双方应事先规定增加的位数,并且年份前必须有正号“+”或负号“-”而不使用“。依据标准,若年份带符号,则前1年为"+0000",前2年为"-0001",依此类推。
- 午夜,一日的开始:完全表示为000000或00:00:00;仅有小时和分表示为0000或00:00
- 午夜,一日的终止:完全表示为240000或24:00:00;仅有小时和分表示为2400或24:00
- 如果时间在零时区,并恰好与UTC相同,那么在时间最后加一个大写字母Z。Z是相对协调世界时时间0偏移的代号。 如下午2点30分5秒表示为14:30:05Z或143005Z;只表示小时和分,为1430Z或14:30Z;只表示小时,则为14Z或14Z。
- 其它时区用实际时间加时差表示,当时的UTC+8时间表示为22:30:05+08:00或223005+0800,也可以简化成223005+08。
日期与时间合并表示时,要在时间前面加一大写字母T,如要表示东八区时间2004年5月3日下午5点30分8秒,可以写成2004-05-03T17:30:08+08:00或20040503T173008+08。
在编写API时推荐使用ISO 8601标准接收参数或响应结果,并且做好时区测试,因为不同编程语言中实现可能有差异。
时区划分和偏移
全球被分为24个时区,每个时区对应一个小时的时间差。 时区划分由IANA维护和管理,其时区数据库被称为 TZ Database(或 Olson Database)。这个数据库包含了全球各个时区的信息,包括时区的名称、标识符、以及历史性的时区变更数据,例如夏令时的开始和结束时间等。在许多操作系统(如Linux、Unix、macOS等)和编程语言(如Java、Python等)中得到广泛应用。
TZ Database具体见我整理的表格,是从Postgresql中导出的一份Excel,关注公众号"程序饲养员",回复"tz"
时区标识符采用"洲名/城市名"的命名规范,例如:"America/New_York"或"Asia/Shanghai"。这种命名方式旨在更准确地反映时区的地理位置。时区的具体规定和管理可能因国家、地区、或国际组织而异。
有一些时区是按照半小时或15分钟的间隔进行偏移的,以适应地理和政治需求。在某些地区,特别是位于边界上的地区,也可能采用不同的时区规则。
EST,CST、GMT(另外一个含义是格林尼治标准时间)这些都是时区的缩写。
这种简写存在重复,如CST 可能有多种不同的含义,China Standard Time(中国标准时间),它对应于 UTC+8,即东八区。Central Standard Time(中部标准时间) 在美国中部标准时间的缩写中也有用。中部标准时间对应于 UTC-6,即西六区。因此在某些软件配置时不要使用简称,一定要使用全称,如”Asia/Shanghai“。
采用东八区的国家有哪些
- 中国: 中国标准时间(China Standard Time,CST)是东八区的时区,对应于UTC+8。
- 新加坡: 新加坡位于东八区,使用UTC+8。
- 马来西亚: 马来西亚的半岛部分和东马来西亚位于东八区,使用UTC+8。
- 菲律宾: 菲律宾采用东八区的时区,对应于UTC+8。
计算机系统中的时间 —— Unix时间戳
Unix时间戳(Unix timestamp)定义为从1970年01月01日00时00分00秒(UTC)起至现在经过的总秒数(秒是毫秒、微妙、纳秒的总称)。
这个时间点通常被称为 "Epoch" 或 "Unix Epoch"。时间戳是一个整数,表示从 Epoch 开始经过的秒数。
一些关键概念:
- 起始时间点: Unix 时间戳的起始时间是 1970 年 1 月 1 日 00:00:00 UTC。在这一刻,Unix 时间戳为 0。
- 增量单位: Unix 时间戳以秒为单位递增。每过一秒,时间戳的值增加 1。
- 正负值: 时间戳可以是正值或负值。正值表示从 Epoch 开始经过的秒数,而负值表示 Epoch 之前的秒数。
- 精度: 通常情况下,Unix 时间戳以整数形式表示秒数。有时也会使用浮点数表示秒的小数部分,以提供更精细的时间分辨率。精确到秒是10位;有些编程语言精确到毫秒是13位,被称为毫秒时间戳。
为什么是1970年1月1日?
这个选择主要是出于历史和技术的考虑。
Unix 操作系统的设计者之一,肯·汤普森(Ken Thompson)和丹尼斯·里奇(Dennis Ritchie)在开发 Unix 操作系统时,需要选择一个固定的起始点来表示时间。1970-01-01 00:00:00 UTC 被选为起始时间。这个设计的简洁性和通用性使得 Unix 时间戳成为计算机系统中广泛使用的标准方式来表示和处理时间。
时间戳为什么只能表示到2038年01月19日03时14分07秒?
在许多系统中,结构体time_t 被定义为 long,具体实现取决于编译器和操作系统的架构。例如,在32位系统上,time_t 可能是32位的 long,而在64位系统上,它可能是64位的 long。 32位有符号long类型,实际表示整数只有31位,最大能表示十进制2147483647(01111111 11111111 11111111 11111111)。
> new Date(2147483647000)
< Tue Jan 19 2038 11:14:07 GMT+0800 (中国标准时间)
实际上到2038年01月19日03时14分07秒,便会到达最大时间,过了这个时间点,所有32位操作系统时间便会变为10000000 00000000 00000000 00000000。因具体实现不同,有可能会是1901年12月13日20时45分52秒,这样便会出现时间回归的现象,很多软件便会运行异常了。
至于时间回归的现象相信随着64为操作系统的产生逐渐得到解决,因为用64位操作系统可以表示到292,277,026,596年12月4日15时30分08秒。
另外,考虑时区因素,北京时间的时间戳的起始时间是1970-01-01T08:00:00+08:00。
好了,关于计算机中的时间就说完了,有疑问评论区相见 或 关注 程序饲养员 公号。