时区这个问题太头疼了,如果你编程,几乎会一定遇到这个问题。对一些时区没有学好的同学,每每碰到心里都会害怕的……其实这个问题很简单,你只知道自己在做什么就好了。
简单的介绍一下时区。中央时间是 UTC 时间,其他时区都是根据这个时间偏移的。中国的时间叫做“北京时间”,时区在“东八区”,记做是 UTC+8. 意思就是我们比中央时间快 8 个小时。我们过着早上9点,中央时间地区的人们过着早上1点(凌晨1点)。为了在程序中方便表达和理解,每个地区会用“区域/位置”格式来表达时区。比如中国的时区可以使用 Asia/Shanghai
,纽约用 America/New_York
表示。
如果你在美国租一台 VPS,那么你将会得到 3 个时间:你在中国写程序,你的电脑上显示的“北京时间”,服务器跑着的是美国时间,然后很多程序会用 UTC 时间来表示。
如果不事先想清楚,你之后肯定会经历很多痛苦来琢磨一个时间到底是 UTC 时间还是美国时间。时间变量传来传去还经常弄丢时区信息,或者转换两次时区,都是很头疼的。我们之前做爬虫,保存数据的抓取时间、更新时间是很重要的,但是数据经过多次存储和处理,忘记了到底在哪里转换过时区,这个时间到底是什么时区的时间。后来开发了一个专门的组件用来处理,这个组件的逻辑又很不清晰,最后此组件的作者离职了,交给我来维护,所以……想象一下我的痛苦吧。
关于时区的最佳实践,和 Python 的 “Unicode” 最佳实践是一样的:三明治模型。
拿到的时间处理成正确的时区,比如抓取了一个中国的网页,时区是北京时间,那么就生成一个北京时间。当数据开始保存的时候,只保存 UTC,这样你就知道,只要是内部系统,保存的时间全都是 UTC 时间。然后时间要进行展示的时候,再进行本地化。这个可以有些框架可以自动完成,比如 django。对于前后端分离的项目,可以考虑用 moment.js 这种框架实现。
另一个关键的点是:知道你的框架是怎么处理时间的。
比如 Django,文档就说的很明白:当开启 USE_TIMEZONE
的时候,Django 会在数据库中存储 UTC 时间,对于 template 和 form Django 会自己在内部进行本地化。
When support for time zones is enabled, Django stores datetime information in UTC in the database, uses time-zone-aware datetime objects internally, and translates them to the end user’s time zone in templates and forms.
有关系统的时间,其实内部是用一个时间戳来计时的。Unix Timestamp 是不带时区信息的,因为这个数字代表的是从1970年(UTC)开始过去了多少秒。所以理论上讲,世界上所有的电脑在内部的计时是一样的,只是显示出来的时间是不同的。如果担心时区混乱,在系统中存储时间戳也是一个不错的方法。
但是需要注意的是,并不是所有的 Timestamp 都是不带时区信息的。我写这篇文章主要是因为今天因为 Celery 的一个时间问题调试了半天。最后发现 Celery 用的时间戳竟然不是 UTC 的,还带时区的信息!具体的代码可以看这里。我实在搞不懂他们在搞毛,为啥不用一个标准的时间戳。
另外 Python 有一个处理时间的库叫 arrow ,这个库因为一个 get()
函数就能处理各种各样的时间格式而受欢迎,之前我们要从网站的文本中提取时间,用了很多这个库。但是我的建议是:千万不要用!这个库的 API 太混乱了,比如 get()
当不写第二个可选参数的时候回忽略第三个参数 tz_info
而导致时区无法识别,写上第二个可选参数第三个参数才可以被识别(后来貌似已经修复了)。
推荐使用 Pendulum 。
另外时间有一个标准的格式,可能你已经见过了,形如这样:2019-01-13T16:27:26+00:00
。这是 ISO 8601 规定的格式。大多数库都能处理这种格式。