转换 Python 中的时区出现意外结果

56

我正在尝试理解为什么在将时区转换为UTC时会得到这些结果:

In [74]: d1 = datetime(2007, 12, 5, 6, 30,tzinfo=pytz.timezone('US/Pacific'))
In [75]: d1
Out[75]: datetime.datetime(2007, 12, 5, 6, 30, tzinfo=<DstTzInfo 'US/Pacific' LMT-1 day, **16:07:00 STD**>)
In [76]: d1.astimezone(pytz.utc)
Out[76]: datetime.datetime(2007, 12, 5, 14, 23, tzinfo=<UTC>)

为什么6:30am变成了2:23pm?

另一方面,如果我使用以下方法,我将得到预期的结果:

In [90]: d2 = datetime(2007, 12, 5, 6, 30)
In [91]: uspac = pytz.timezone('US/Pacific')
In [92]: d2_aware = uspac.localize(d2)
In [94]: d2_aware.astimezone(pytz.utc)
Out[94]: datetime.datetime(2007, 12, 5, 14, 30, tzinfo=<UTC>)

一些关于这个问题的旧版本:Python datetime对象显示错误的时区偏移pytz的奇怪时区问题 - FObersteiner
5个回答

63

我的做法只是一个权宜之计,简单的规则是不要使用datetime()创建带有时区信息的日期时间

这个示例可以给你一个提示。正如你所看到的,你可以避免意外的差异,只要你制作“naive”日期时间(即没有时区信息的日期时间),然后将其本地化(当你在UTC上创建日期时间时,不适用):

import pytz
from datetime import datetime

# make Jan 1 on PDT -> UTC
pdt = pytz.timezone("America/Los_Angeles")
pdtnow1 = datetime(2014,1,1, tzinfo=pdt)
pdtnow2 = pdt.localize(datetime(2014,1,1))
pytz.utc.normalize(pdtnow1)
# > datetime.datetime(2014, 1, 1, 7, 53, tzinfo=<UTC>)
pytz.utc.normalize(pdtnow2)
# > datetime.datetime(2014, 1, 1, 8, 0, tzinfo=<UTC>)

# make Jan 1 on UTC -> PDT
utcnow1 = datetime(2014,1,1, tzinfo=pytz.utc)
utcnow2 = pytz.utc.localize(datetime(2014,1,1))
pdt.normalize(utcnow1)
# > datetime.datetime(2013, 12, 31, 16, 0, 
# > tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
pdt.normalize(utcnow2)
# > datetime.datetime(2013, 12, 31, 16, 0, 
# > tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)

13
谢谢您提供的例子,让我不再觉得自己疯了,因为每次看到我的结果少了几分钟,我都会有这种感觉。 - punkrockpolly
@punkrockpolly 不客气! - Kenial
5
我在网上搜索偏移量为7:53时,找到了这个问题。我推测现在美国太平洋时间已经开始实行夏令时,所以偏移量增加了七分钟,而不是一小时?多么奇怪! - Michael Scheper
谢谢。这太神奇了。我不明白为什么我的代码会慢了7分钟... - Aerodyno
如果你的日期年份早于1901年,那么分钟可能仍会出现问题,例如:pdt.localize(datetime(1900,1,1)) - Roshin Jay
显示剩余2条评论

43
从部分文档中得知: http://pytz.sourceforge.net/#localized-times-and-date-arithmetic 不幸的是,对于许多时区,使用标准datetime构造函数的tzinfo参数与pytz一起“不起作用”。但对于没有夏令时转换的时区(如UTC),这是安全的。处理时间的首选方法是始终使用UTC进行操作,在生成供人类阅读的输出时才转换为本地时间。

谢谢!我真的想不出来这个问题的解决方法! - Inti
1
不幸的是,“对于没有夏令时转换的时区来说是安全的”这个说法过于乐观了。它也无法处理那些固定偏移量随着时间改变的时区。幸运的是,UTC没有改变过。 - Mark Ransom

9

很遗憾,使用此方法创建有时区意识的日期不起作用。

如果您使用Django,则可以使用实用程序函数make_aware以正确的方式执行此操作。

from django.utils.timezone import make_aware
from pytz import timezone

unaware_datetime = datetime(2007, 12, 5)
local_datetime = make_aware(datetime(2007, 12, 5))
specific_datetime = make_aware(datetime(2007, 12, 5), timezone("Australia/Melbourne"))

如果您没有使用Django,那么make_aware函数的源代码可能会给您带来灵感。

1
Django的make_aware(dt)只是调用了pytz.timezone(...).localize(dt) - Dispenser

4
我正在重新审视有关日期和时间的一些问题,以查看一些更新的库是否在这种情况下更有帮助(或不是)。 Pendulum 是一种将时区与日期和时间一起存储的库,使其在这种情况下特别有价值。
>>> import pendulum
>>> d1 = pendulum.datetime(2007,12,5,6,30, tzinfo='US/Pacific')
>>> d1
<Pendulum [2007-12-05T06:30:00-08:00]>
>>> d1.timezone
<Timezone [US/Pacific]>
>>> d1.astimezone(tz='UTC')
<Pendulum [2007-12-05T14:30:00+00:00]>

许多其他甜美的功能也很不错。

0

.astimezone之前打印d2_aware,你会看到PST-1(太平洋标准时间),但在第一个例子中,你有LMT-1(本地平均时间) - 可能会有7分钟的差异。

但我不知道为什么pytz使用不同的时区。


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