关于时区在开发中的一些思考
过去三年我在做一个面向港澳台和日本的未上线项目,关于「时区」这个概念,在做开发的过程中会有些抽象,一直觉得对于时区的处理很混乱(不管是代码、服务器设置,还是运营需求)。
今年休假去日本玩了一趟,这是我人生中第一次离开东八区,因为只是短暂的旅途,跟国内的联系还是非常紧密的,所以切实体会到了跨时区带来的一些不同体验。
于是决定写下这篇关于「时区」的思考的文章,可能会对各位做开发的朋友有一点帮助 :-P
当我们只需要考虑一个时区的时候,事情其实很简单,比如说我落地东京后手机会自动切换到日本标准时间(JST
,Japan Standard Time),因为车票、酒店入住/离店时间、景点开放时间等等都是用的 JST,这时候跟国内生活其实完全没差,几乎没有适应成本。
但当你需要和国内的朋友联系,或者使用国内的服务时,就会容易有一点点不一样的体验:
- 找同事的时候,公司 IM 有显示同事当前时区的时间,很直观同事是不是在工作时间
- 云闪付的优惠券是在北京时间(
BJT
) 10:00 开始抢的,而我在 JST 的 10:00 打开发现还差一小时,等到了 BJT 的 10:00,我又忘了这事 - 网易云音乐的签到是 BJT 0:00:00~23:59:59,而客户端没有处理好这个问题,在 JST 0:00 的时候就显示可以签到,但点击之后后端返回重复签到
- ...
通过上面的例子可以看到,时区问题的核心就是给用户展示正确的时间。作为开发者的我们,以下几点是可以参考的:
1. 获取本地时间记得要带上时区
如果获取客户端时间只是简单粗暴地获取 年-月-日 时:分:秒
,那么就可能会出现客户端显示时间到了,而下一步跟后端交互的时候却有报错;所以最佳做法是同时获取时区并换算到后端/运营使用的时区;同样的,服务端获取时间也需要带上时区
2. 确定运营时区
服务器时区一般是跟着机器所在地或者统一用 UTC 的,我之前做的项目是按地区分服务器的,且不同地区的用户数据不互通,这种情况服务端就不需要额外处理,而运营按照当地时间进行即可;
而如果全球用户是互通的(例如邮件、在线会议等等),这种情况就会相对复杂,通常会使用 UTC 时间来做换算;也可以按照用户所在时区的绝对时间,但是要处理好幂等问题
3. 时间存储带上时区
在 MySQL 中,DATETIME
类型是不带时区信息的,而 TIMESTAMP
是带时区(UTC)的;或者也可以直接存 UNIX 时间戳,因为 UNIX 时间戳的定义是 从 UTC 1970 年 1 月 1 日 0 时 0 分 0 秒起至现在的总秒数
,比如说 0
这个 UNIX 时间戳,对应的是北京时间 1970-01-01 08:00:00
或者是日本标准时间的 1970-01-01 09:00:00
,它其实已经隐含了时区信息
题外话
前段时间去了成都和上海,体感上成都比广州晚半小时天黑、广州比上海晚半小时天黑,当时和朋友简单讨论了下中国东西跨度那么大却只用一个时区的优缺点。
简单来说就是一个时区可以少了换算,让很多事情没有那么复杂,缺点就是如果大家都按照北京时间来生活开展生产工作,会让西部地区的同胞生物钟比较难受——太阳在晚上八九点才下山和在早上八九点才开始升起。
其实中国以前是有多个时区的,也使用过夏令时,后来因为各种困扰统一使用了我们现在最熟悉的北京时间(新疆民间采用新疆时间(UTC+6),而在铁路、航空和邮电等业务上继续采用北京时间,因为新疆我没有去过没有切身体会,这里就不展开讲了,但不影响这篇文章的其他内容)
只要我们在写代码的时候也稍微注意一下时区问题,跨时区也不是什么太大的问题 :-P