使用Python比较UTC时间和东部时间的方法

6

我正在使用Python中的datetime模块比较两个时间,但我似乎无法创建一个时区感知的UTC时间time对象。

>>> import pytz, datetime
>>> UTC_TZ = pytz.utc
>>> EASTERN_TZ = pytz.timezone('America/New_York')
>>> d1 = datetime.time(10, tzinfo = UTC_TZ)
>>> d1
datetime.time(10, 0, tzinfo=<UTC>)
>>> d2 = datetime.time(10, tzinfo = EASTERN_TZ)
>>> d2
datetime.time(10, 0, tzinfo=<DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD>)
>>> d1 < d2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware times

这是一个bug吗?我需要使用特殊的UTC时区吗?发生了什么事情?

3个回答

9

感谢wberry为解决这个问题做出的贡献,但为了简明扼要地回答,我将在此总结:

根据datetime文档,在比较两个datetime.time对象时:"如果两个操作数都是aware且具有不同的tzinfo属性,则首先通过减去它们的UTC偏移量(通过self.utcoffset()获得)来调整操作数"

在您提供的示例中,由于EASTERN_TZ.utcoffset()返回None,因此比较会抛出TypeError。utcoffset为None,因为美国东部观察夏令时,因此与UTC的时间偏移量取决于在datetime.time中不可用的日期。

您应该使用datetime.datetime对象进行跨时区比较:

>>> import pytz, datetime
>>> UTC_TZ = pytz.utc
>>> EASTERN_TZ = pytz.timezone('America/New_York')
>>> d1 = datetime.datetime(2012, 1, 1, 10, 0, tzinfo=UTC_TZ)
>>> d2 = datetime.datetime(2012, 1, 1, 10, 0, tzinfo=EASTERN_TZ)
>>> d1 < d2
True

1
在处理带有夏令时的时区时,不应使用 tzinfo 参数。使用 EASTERN_TZ.localize(naive_dt, is_dst=None).astimezone(pytz.utc) 获取日期时间对象进行比较。.astimezone() 不是必需的,但最好始终在内部使用 UTC 时间并将其转换为其他时区仅用于 IO。 - jfs
1
请注意,在3.3版本中,naive时间实例和aware时间实例之间的相等比较不会引发TypeError异常。 - kolypto

3
你收到此错误是因为你试图测量一个可以绑定到特定UTC时刻的time对象和另一个“naive”(无法绑定到特定UTC时刻)的time对象之间的差异。解决方法是将两个比较对象都变成offset-aware或都设置为naive。
以下示例使用datetime对象,但基本思路相同。
import datetime, time, pytz

EST = pytz.timezone('America/New_York')
UTC = pytz.timezone('Etc/UTC')
dt1 = datetime.datetime.fromtimestamp(time.time(), EST)
# ... time passes
dt2 = datetime.datetime.fromtimestamp(time.time(), UTC)
elapsed = dt2 - dt1

那么这是 Python 中的一个 bug 吗?文档中说:“类型为 time 或 datetime 的对象 d 可能是 naive 或 aware。如果 d.tzinfo 不是 None 且 d.tzinfo.utcoffset(d) 不返回 None,则 d 是 aware 的。如果 d.tzinfo 为 None,或者如果 d.tzinfo 不为 None 但 d.tzinfo.utcoffset(d) 返回 None,则 d 是 naive 的。” 如果这是正确的,那么为什么我的示例中 d1 表示它是 offset-naive 的呢? - Chris B.
@ChrisB.,测试UTC_TZ并查看它是否对utcoffset返回None - Mark Ransom
4
我猜是因为对于America/New_York,无法仅通过一个time对象计算出UTC偏移量;你需要一个datetime来确定是否启用了夏令时。 - wberry
如果dt1dt2之间存在闰秒,那么“elapsed”可能不太精确,例如:“2012年7月1日00:00:00 UTC”-“2012年6月30日23:59:59 UTC”等于2而不是1。虽然在大多数情况下这并不重要。 - jfs
是的,pytz UTC 实际上是 Unix 时间,需要进行修正。 - wberry
显示剩余2条评论

1

我猜问题在于UTC被认为是没有时区或“偏移量无关”的,也许是这样吧?我建议在进行任何比较之前将所有内容转换为UTC。

你需要知道输入和输出的时区,但你应该尽可能地保持内部表示都在UTC,并且只存储每个用户的时区并在需要时进行转换。从长远来看,这将节省很多麻烦。

此外,你不应该这样做。最好使用

timezone.localize(dt)

如下所述:http://pytz.sourceforge.net/#localized-times-and-date-arithmetic


1
EASTERN_TZ.localize(datetime.time(10)) 给我返回了 TypeError: 不支持的操作数类型 '+': 'datetime.time' 和 'datetime.timedelta' - Chris B.
@ChrisB.:相同的时区在不同的日期可能具有不同的UTC偏移量,即tzz + datetime.time()不足以找到正确的UTC偏移量,需要使用tz + datetime.datetime(假设给定时间存在且不含歧义)。 - jfs

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