pytz本地化与datetime替换
我在使用pytz的.localize()函数时遇到了一些奇怪的问题。有时候它不会对本地化的日期时间进行调整:
.localize的表现:
>>> tz
<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD>
>>> d
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421)
>>> tz.localize(d)
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421,
tzinfo=<DstTzInfo 'Africa/Abidjan' GMT0:00:00 STD>)
>>> tz.normalize(tz.localize(d))
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421,
tzinfo=<DstTzInfo 'Africa/Abidjan' GMT0:00:00 STD>)
如你所见,经过localize/normalize操作后,时间并没有发生变化。不过,如果使用.replace:
>>> d.replace(tzinfo=tz)
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421,
tzinfo=<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD>)
>>> tz.normalize(d.replace(tzinfo=tz))
datetime.datetime(2009, 9, 2, 15, 1, 42, 91421,
tzinfo=<DstTzInfo 'Africa/Abidjan' GMT0:00:00 STD>)
这似乎会对日期时间进行调整。
问题是 - 哪个是正确的,为什么另一个是错的?
4 个回答
这个 DstTzInfo 类是用来处理那些在某些时间点上与协调世界时(UTC)偏移量会变化的时区。比如,大家可能知道,很多地方在夏天开始时会进入“夏令时”,然后在夏天结束时又回到“标准时间”。每个 DstTzInfo 实例只代表一个这样的时区,但“localize”和“normalize”这两个方法可以帮助你得到正确的实例。
以阿比让(Abidjan)为例,根据 pytz 的资料,这里只经历过一次时区的变化,那是在 1912 年:
>>> tz = pytz.timezone('Africa/Abidjan')
>>> tz._utc_transition_times
[datetime.datetime(1, 1, 1, 0, 0), datetime.datetime(1912, 1, 1, 0, 16, 8)]
我们从 pytz 得到的 tz 对象代表的是1912年之前的时区:
>>> tz
<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD>
现在看看你给的两个例子,当你调用 tz.localize(d) 时,你并不会把这个1912年之前的时区加到你的 naive datetime 对象上。它假设你给它的 datetime 对象代表的是在那个本地时间的“正确时区”下的时间,也就是1912年之后的时区。
但是在你第二个例子中,使用 d.replace(tzinfo=tz),它把你的 datetime 对象当作是代表1912年之前的时区的时间。这可能不是你想要的。然后当你调用 dt.normalize 时,它会把这个时间转换成适合那个 datetime 值的时区,也就是1912年之后的时区。
localize
是一个正确的函数,用来创建带有固定日期时间值的日期时间对象。使用这个函数后,得到的日期时间对象会保留原来的日期时间值。在我看来,这是一种非常常见的用法,也许 pytz 可以对此做更好的说明。
replace(tzinfo = ...)
这个名字起得不太好。这个函数的行为有点随机。我建议不要用这个函数来设置时区,除非你喜欢自找麻烦。我已经因为使用这个函数受够了。
localize
这个函数只是简单地认为你传入的日期时间是“正确的”(除了它不知道时区之外!),所以它只会设置时区,不会做其他调整。
你可以(而且建议这样做……)在内部使用UTC时间(而不是使用没有时区信息的日期时间),当你需要以本地化的方式处理日期时间时,可以使用replace
。另外,normalize
可以处理夏令时等情况。