在python中转换时区的意外结果
Posted
技术标签:
【中文标题】在python中转换时区的意外结果【英文标题】:unexpected results converting timezones in python 【发布时间】:2015-12-17 09:05:59 【问题描述】:我试图了解为什么在将时区转换为 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:30 变成了下午 2:23?
另一方面,如果我使用以下方法,我会得到预期的结果:
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>)
【问题讨论】:
【参考方案1】:很遗憾,使用此方法创建时区感知日期不起作用。
如果您使用的是 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 函数的 source code 可能会给你灵感。
【讨论】:
Django 的make_aware(dt)
只是调用pytz.timezone(...).localize(dt)
【参考方案2】:
我正在重新审视有关日期和时间的一些问题,以查看一些较新的库是否证明在这种情况下(或没有)更有帮助。 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]>
还有很多其他有趣的功能。
【讨论】:
【参考方案3】:我得到的只是一种解决方法,简单的规则是永远不要使用 datetime() 创建带有时区信息的日期时间。
此示例将为您提供提示。如您所见,您可以避免意外的差异,只有您制作“天真”日期时间(即没有时区信息的日期时间)然后对其进行本地化(但在 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>)
【讨论】:
当我看到我的结果持续几分钟时,我以为我疯了。非常感谢这个例子。 @punkrockpolly 我的荣幸! 网络搜索 7:53 的偏移量帮助我找到了这个问题。我想,既然美国太平洋时间已经开始夏令时,七分钟不知何故被添加到偏移量中,而不是一个小时?多么奇怪! 这部分 python 对我来说似乎是 pythonic 太乱了 两次转换时区还是有问题pdt.localize(datetime(2014, 1, 1) \ .astimezone(other_timezone)
【参考方案4】:
来自部分文档: http://pytz.sourceforge.net/#localized-times-and-date-arithmetic
不幸的是,使用标准日期时间构造函数的 tzinfo 参数对许多时区的 pytz “不起作用”。 [...] 但是对于没有夏令时转换的时区来说是安全的,例如 UTC。 [...] 处理时间的首选方式是始终以 UTC 工作,仅在生成供人类阅读的输出时转换为本地时间。
【讨论】:
谢谢!我这辈子都想不通! 不幸的是,“没有夏令时转换的时区是安全的”这句话过于乐观了。对于固定偏移量多年来发生变化的时区,它也失败了。幸运的是 UTC 没有改变。【参考方案5】:在.astimezone
之前打印d2_aware
,您会看到PST-1
(太平洋标准时间),但在第一个示例中,您有LMT-1
(本地标准时间)- 并且可能会有7 分钟的差异。
但我不知道为什么pytz
使用不同的时区。
【讨论】:
以上是关于在python中转换时区的意外结果的主要内容,如果未能解决你的问题,请参考以下文章
在 C++ 中使用 istringstream 进行字符串到双重转换的意外结果
将灰度图像中的像素转换为黑色(OpenCV)会导致意外结果?