【现象描述】
python 的 datetime replace timezone 为 ‘Asia/Shanghai' 和 ’PRC‘,然后取得的时间戳不正确。
print(datetime.now().replace(tzinfo=pytz.timezone('PRC')).timestamp())
print(datetime.now().replace(tzinfo=pytz.timezone('Asia/Shanghai')).timestamp())
print(datetime.now().timestamp())
前两个值一致;比第三个值小了几百秒;
【正确性】
第三个值是正确的。
【问题】
- 为什么?
- <font color="red">如果系统环境时区不是 UTC+8 的话,而输入的“日期时间字符串”是北京时间,那么怎么求正确的时间戳???</font>
亚洲/上海 or 中华人民共和国(PRC)的 pytz.timezone 和 「北京时间」有 6 分钟偏移。
原因有历史原因,可以搜索到!
所以在处理“北京时间”的时候,需要掌握一定的知识。
以下内容都默认输入的时间(字符串等)都是北京时间,而且不带时区后缀
也就是说,datetime.datetime 没有指定时区的话,得到的 datetime.datetime 实例使用的时区(默认时区)就是 Python 运行环境的时区(一般就是操作系统设定的时区)。
所以,如果你在中国使用电脑,电脑设定的时区是中国,你创建的 datetime.datetime 实例中的时间是北京时间,那么:
instance.timestamp() # instance: <datetime.datetime>
得到的就是正确的时间戳。
作为一名软件工程师,应当要考虑到自己写的代码不止在一处电脑运行;所以不能绝对保证 Python 的运行环境时区就是中国的(生产环境上,这一点应该有运维保证)。
那么,对于不含有指定时区的「日期时间」输入,但我们要将这个输入作为【北京时间】处理的时候,就应该多敲几个字符 ensure 得到的 datetime.datetime 实例上的时区信息是 UTC+8:00 的,怎么做到这一点?
看如下代码:
这段代码直接 .timestamp() 取了时间戳
各位也可以去掉 .timestamp() 看出来
pytz.timezone(<时区>).localize(<datetime.datetime>)
和
datetime.datetime.replace(tzinfo=pytz.timezone(<时区>))
得到的 datetime.datetime 实例结果中,tzinfo 的不同(自己试一下)。
即,使用 .localize 的方式才是处理【北京时间】的正确方式!
之所以这么做,是因为这个帖子,原本的问题是得到 Unix timestamp【时间戳】的问题;
在 Python 中,【过去日期时间】的时间戳使用借由 datetime.datetime 获得是很合理的方式!
获取时间戳的方式就是
instance.timestamp() # instance: <datetime.datetime>
但是这么获取时间戳的时候,python 需要知道这个 datetime.datetime 的 instance 是哪个时区的,才能计算时间戳;默认时区的方式我在 1. 中已经说了,如果没理解请回到 1. 点再看一下。
但是如果我们考虑默认时区不是 UTC+8:00 的话,那么从 datetime.datetime 实例计算时间戳就会有问题;所以我们可以通过 2. 中的两种方式(
.replace
,.localize
) 来得到含有【中国时区】(tzinfo)的 datetime.datetime 实例,然后由这个实例计算时间戳就是正确的了。而因为历史原因,所以
.replace
的解决方案会有 6 分钟的 offset,所以,实际上我们处理【北京时间】的正确方式应当是.localize
的方式。最后,即使不是求【时间戳】,我们可能需要处理一个【日期时间】字符串为 Python 的 datetime.datetime 实例,那么也应该使用
.localize
为其添加上 tzinfo 信息(UTC+8:00)