一文读懂全球化系统中的日期时间处理问题( 二 )


2.4 万能的 Unix TimestampUnix Timestamp 在存储、计算、传递环节都可以使用,可谓万能 。它唯独不适合表达纪念日日期 。
它通过一个数值表示了一个绝对时间与 Unix Epoch 时间(定义为 1970-01-01T00:00:00Z)的差值秒数 。Unix Timestamp 本身已经表达了绝对时间,并不需要时区信息 。
使用 Unix Timestamp 时,应特别注意选用合适的数值类型,它会影响时间表示的范围 。稍不留神,你就可能种下一个新的千年虫 。

  • 用有符号int32,最多表示到 2038 年 。MySQL 的 TIMESTAMP 类型也是它,一个千年虫变种
  • 用有符号int64,并使用 9 位 10 进制定点小数位时,就是 Golang 的UnixNano(),可以表示 1678 年至 2262 年
  • 一般不会用浮点数表示,因为浮点数的精度不固定
3. 产品视角的日期时间设计本着不重不漏的原则,我们可以按如下表格划分产品中的所有日期时间对象:
 
日期+时间
仅时间
仅日期
不指明时区,无需根据用户所在时区做转换
① 表示本地的确定时间点
③ 表示本地重复性时间
⑤ 表示纪念日、节日
指明时区,需根据用户所在时区做转换
② 表示全球唯一确定时间点
④ 表示全球可理解的重复性时间
? 不存在的场景
下面逐一解释这五种场景 。
3.1 表示全球唯一确定时间点(表中的 ②)信息量包含「年月日-时分秒-时区」 。这样,就可以完全确定历史长河中的一个无歧义的时间点 。这个时间点是完全客观的,和访问的用户地理位置无关,和服务器的地理位置无关,和什么都无关 。
产品表现上,通常会根据查看者所在的时区来重新调整时间的显示 。
用途举例:
  • 单个事件发生的时间 。如 2022 年冬奥会开幕式的时间:2022 年 2 月 4 日,20 点整,+0800 时区 。一个英国人看电视转播预告时,会看到开幕式的转播时间是:2022 年 2 月 4 日中午 12 点整 。这体现了根据查看者做时间的转换 。
3.2 表示本地的确定时间点(表中的 ①)包含「年月日-时分秒」,因为没有时区信息,所以它本身并不能确定一个精确的时间点,而是只在特定的情境下才有意义 。
所谓特定的情境,是因为业务场景中蕴含了时区的信息,并且是大家公认的共识 。因此,本质上它仍然表示了一个绝对时间 。在产品表现上,因为对时区的共识,所以不需要根据查看者的时区来调整时间的展示 。
用途举例:
  • 在非国际化的产品中,明确知道用户所在的时区,那么去掉时区是最简单的处理方式,可以省去很多麻烦 。
  • 对于时区有其他约定俗成的理解 。例如:飞机的起飞降落时间,酒店的入住离店时间,一定是按照飞机起落地、酒店坐落地当地时区来表达的 。在所有订票网站上,都会按照这个规则显示时间,不论访问的用户身处哪个时区 。
3.3 表示重复性时间(表中的 ③ 和 ④)和前两类相比,去掉了「日期」这个信息,是为了描述重复性的日程 。它可以是指明了时区的,也可以不指明时区,而基于人们对时区的共识去理解 。
用途举例:
  • 每周三 8:00+0800 开会,如果这可以是个跨国的会议,大家都能理解正确的时间 。这时,产品表现上应该注意根据查看者来调整显示 。
  • 每周三 8:00 起飞的航班,航班起飞地的时区是蕴含的共识 。产品表现中不必根据查看者的时区调整显示 。
3.4 纪念日日期(表中的 ⑤)日期对象几乎只有一个有意义的用途:表示纪念日/节日 。它不会包含时区信息 。
认为「日期」只能用于「纪念日」,有些绝对了 。但我确实查阅了很多资料,也没有看到任何非「纪念日」用途的日期 。
例如:
  • 小吴的生日是 3 月 11 日,那么不管他在中国还是美国,都会在 3 月 11 日这一天过生日 。
  • 每年 12 月 25 日是西方的圣诞节,各个国家都在 12 月 25 日这一天庆祝,虽然它们并不在同一个时区 。
产品体现上,不需要根据时区调整日期的显示 。本质上,「纪念日」的逻辑,其实是人脑的不严谨导致的一种习惯,是不严谨、不客观的习惯 。不包含时区信息,就是为了满足这种不严谨的习惯 。
3.5 区分「纪念日日期」与「精度不高的绝对时间」上面说过,日期对象不能包含时区 。你可能会问,我需要表示“北京时间 2022 年 3 月 22 日”呢?答案是:这不是一个日期,而是一个「精度不高的绝对时间」 。


推荐阅读