Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
432 views
in Technique[技术] by (71.8m points)

datetime.replace(tzinfo=pytz.timezone('Asia/Shanghai’) 后时间戳变小

【现象描述】

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())

前两个值一致;比第三个值小了几百秒;

【正确性】

第三个值是正确的。

【问题】

  1. 为什么?
  2. <font color="red">如果系统环境时区不是 UTC+8 的话,而输入的“日期时间字符串”是北京时间,那么怎么求正确的时间戳???</font>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

亚洲/上海 or 中华人民共和国(PRC)的 pytz.timezone 和 「北京时间」有 6 分钟偏移。

原因有历史原因,可以搜索到!

所以在处理“北京时间”的时候,需要掌握一定的知识。

以下内容都默认输入的时间(字符串等)都是北京时间,而且不带时区后缀

  1. 使用 Python 的 datetime.datetime(<北京时间>).timestamp() 得到的是正确的时间戳(timestamp);除非你电脑的系统时区不是中国的。
    也就是说,datetime.datetime 没有指定时区的话,得到的 datetime.datetime 实例使用的时区(默认时区)就是 Python 运行环境的时区(一般就是操作系统设定的时区)。
    所以,如果你在中国使用电脑,电脑设定的时区是中国,你创建的 datetime.datetime 实例中的时间是北京时间,那么:
    instance.timestamp() # instance: <datetime.datetime>
    得到的就是正确的时间戳。
  2. 作为一名软件工程师,应当要考虑到自己写的代码不止在一处电脑运行;所以不能绝对保证 Python 的运行环境时区就是中国的(生产环境上,这一点应该有运维保证)。
    那么,对于不含有指定时区的「日期时间」输入,但我们要将这个输入作为【北京时间】处理的时候,就应该多敲几个字符 ensure 得到的 datetime.datetime 实例上的时区信息是 UTC+8:00 的,怎么做到这一点?
    看如下代码:

    >>> import datetime as dt
    >>> import pytz
    >>> pytz.timezone('Asia/Shanghai').localize(
    ... dt.datetime(2020, 9, 25, 18, 12, 23)
    ... ).timestamp() # correct way
    1601028743.0
    >>>
    >>> dt.datetime(2020, 9, 25, 18, 12, 23).replace(
    ... tzinfo=pytz.timezone('Asia/Shanghai')
    ... ).timestamp() # 00:06 offset
    1601028383.0

    这段代码直接 .timestamp() 取了时间戳
    各位也可以去掉 .timestamp() 看出来
    pytz.timezone(<时区>).localize(<datetime.datetime>)

    datetime.datetime.replace(tzinfo=pytz.timezone(<时区>))
    得到的 datetime.datetime 实例结果中,tzinfo 的不同(自己试一下)。

    即,使用 .localize 的方式才是处理【北京时间】的正确方式!

  3. “ensure 得到的 datetime.datetime 实例上的时区信息是 UTC+8:00”
    之所以这么做,是因为这个帖子,原本的问题是得到 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)


除了 pytz 还有其它库可以处理时区;这里不再做展开。

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...