Python(datetime)时区转换偏差4分钟

11

当我运行这段代码时:

#!/usr/bin/env python3
from datetime import datetime, timedelta
from dateutil import tz
from pytz import timezone

time = "2020-01-15 10:14:00"
time = datetime.strptime(time, "%Y-%m-%d %H:%M:%S")

print("time1 = " + str(time))

time = time.replace(tzinfo=timezone('America/New_York'))
print("time2 = " + str(time))

time = time.astimezone(tz.gettz('UTC')) # explicity convert to UTC time
print("time3 = " + str(time))

time = datetime.strftime(time, "%Y-%m-%d %H:%M:%S")  # output format
print("done time4 = " + str(time))

我得到了这个输出:

time1 = 2020-01-15 10:14:00
time2 = 2020-01-15 10:14:00-04:56
time3 = 2020-01-15 15:10:00+00:00
done time4 = 2020-01-15 15:10:00

我本以为最终时间应该是“2020-01-15 15:14:00”,有人知道为什么会慢了4分钟吗?我不明白为什么time2的偏差是“-04:56”,而不是“-05:00”。


2
2021年:由于我们已经使用Python 3.9有一段时间了,因此可以通过使用zoneinfo来避免pytz的“本地化陷阱”,例如请查看https://dev59.com/VHM_5IYBdhLWcg3wZSI6#63628816。 - FObersteiner
2个回答

13

来自pytz文档

该库与Python API中记录的tzinfo实现不同;如果要创建本地时钟时间,您需要使用本文档中记录的localize()方法。此外,如果在跨越夏令时边界的本地时间上执行日期算术运算,则结果可能在错误的时区(即从2002-10-27 1:00 EST减去1分钟,您会得到2002-10-27 0:59 EST而不是正确的2002-10-27 1:59 EDT)。

因此,您正在错误地使用pytz。

下面展示了您使用pytz (datetime.replace(tzinfo=pytz.timezone)) 的正确和错误代码,以及使用datetime和pytz的推荐方式(pytz.timezone.localize(datetime))。

from datetime import datetime, date, time, timezone
from dateutil import tz
import pytz


d = date(2019, 1, 27)
t = time(19, 32, 00)

t1 = datetime.combine(d, t)
t1_epoch = t1.timestamp()
print("t1_epoch " + str(t1_epoch))
print("t1 " + str(t1))


# your approach/code
nytz = pytz.timezone('America/New_York')
t3 = t1.replace(tzinfo=nytz)
t3_epoch = t3.timestamp()
print("t3_epoch " + str(t3_epoch))
print("t3 " + str(t3))

# recommended approach/code using localize
nytz = pytz.timezone('America/New_York')
t6 = nytz.localize(t1)
t6_epoch = t6.timestamp()
print("t6_epoch " + str(t6_epoch))
print("t6 " + str(t6))

以上代码的输出结果:
t1_epoch 1548617520.0
t1 2019-01-27 19:32:00
t3_epoch 1548635280.0
t3 2019-01-27 19:32:00-04:56
t6_epoch 1548635520.0
t6 2019-01-27 19:32:00-05:00

t3是您正在进行的操作,但它提供了不正确的偏移量(-4:56)。请注意,在这种情况下POSIX时间也是不正确的。根据定义,POSIX时间不随时区而改变。

t6使用pytz.timezone.localize()方法创建,并提供了正确的UTC偏移量(-5:00)。

更新:根据一个用户发现答案令人困惑,更新了答案的语言。


1
虽然正确,但答案对我来说似乎有些混淆(正确和错误的代码?)。基本上,答案是:使用pytz模块的timezone类中的localize()而不是datetime类中的replace()。顺便说一下,要检查UTC偏移量,您可以简单地使用dt.utcoffset() / timedelta(hours=1)(假设dt是一个知道时区的datetime对象)。 - FObersteiner
@MrFuppes 我已经更新了“正确和错误的代码”部分,以避免混淆(虽然有一个串行逗号来消除歧义)。是的,我们可以使用您建议的代码获取偏移量。OP已经在使用str(datetime.datetime),因此将其显示为偏移量是有意义的。 - narendra-choudhary
@narendra-choudhary 你是最棒的!非常感谢 :) - Daniel E

4

我知道这是一个老帖子,但今天我遇到了完全相同的问题,当将日期从 America/Sao_Paulo 时区转换为 UTC 前后。但对于我的情况,它偏差了 6 分钟。

合并 @narendra-choudhary 提供的解决方案,我得到了这个:

import pytz
from datetime import datetime
# Simulating unaware date created by user in front-end
in_date = datetime.now()
z = pytz.timezone("America/Sao_Paulo")
aware_localized_date = z.localize(in_date)
# Now converting to UTC
utc_date = aware_localized_date.astimezone(pytz.UTC)
print(utc_date.strftime("%d/%m/%Y %H:%M:%S"))
print(in_date.strftime("%d/%m/%Y %H:%M:%S"))

输出结果为:

>>> print(utc_date.strftime("%d/%m/%Y %H:%M:%S"))
11/06/2021 21:56:02
>>> print(in_date.strftime("%d/%m/%Y %H:%M:%S"))
11/06/2021 18:56:02
>>>

多亏了 @narendra-choudhary,我的代码现在可以工作了!希望对那些感到困惑的人有所帮助。


Python日期时间时区转换有误,比实际时间晚了6分钟,希望Google的间谍机器人能够解决这个问题。 - Renato Aloi
1
使用 datetime.now(pytz.timezone("America/Sao_Paulo")) 是安全且更简单的方式之一,您无需明确使用 pytz 的时区类进行本地化。 - FObersteiner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接