两个 timedelta 对象的差异,带有时区

Difference of two timedelta objects, with timezones

我想计算一个日期间隔中有多少小时:例如“2014.03.29-30”应该是 47,因为夏令时。

我的方法是制作两个日期时间对象,在以下示例中:

datetime.datetime(2014, 3, 29, 0, 0, tzinfo=<DstTzInfo 'Europe/Budapest' LMT+1:16:00 STD>)
datetime.datetime(2014, 3, 30, 23, 59, tzinfo=<DstTzInfo 'Europe/Budapest' LMT+1:16:00 STD>)
return (date2-date1) + timedelta(minutes=1)

然而,它给出了“2 天,0:00:00”,这是不正确的。我如何制作一个考虑时区和 dst 的 timedelta 对象?另外,如果整个问题有更简单的解决方案,我愿意接受。

谢谢!

在 1901-12-13 20:45:52 UTC 之前,'Europe/Budapest' 时区是 LMT+1:16:00 STD。 目前,截至 2016 年 5 月 5 日,'Europe/Budapest' 时区为 CET+2:00:00 DST。

如果您使用 pytz 的 localize method,那么 pytz 将为 'Europe/Budapest' 选择适合给定原始日期时间的时区(utcoffset 和 dstoffset):

import datetime as DT
import pytz

tzone = pytz.timezone('Europe/Budapest')
date1 = tzone.localize(DT.datetime(2014, 3, 29, 0, 0), is_dst=None)
# datetime.datetime(2014, 3, 29, 0, 0, tzinfo=<DstTzInfo 'Europe/Budapest' CET+1:00:00 STD>)

相反,如果您直接将 tzinfo=tzone 提供给 datetime.datetime,如:

wrong_date1 = datetime.datetime(2014, 3, 29, 0, 0, tzinfo=tzone)
# datetime.datetime(2014, 3, 29, 0, 0, tzinfo=<DstTzInfo 'Europe/Budapest' LMT+1:16:00 STD>)

然后 datetime.datetime 错误地选择了与 'Europe/Budapest' 关联的第一个时区 ,无论那是否是对 2014-3-29 的影响.

因此,在使用 pytz 时,请始终使用 tzone.localize 使天真的日期时间感知时区:

import datetime as DT
import pytz
tzone = pytz.timezone('Europe/Budapest')
date1 = tzone.localize(DT.datetime(2014, 3, 29, 0, 0), is_dst=None)
date2 = tzone.localize(DT.datetime(2014, 3, 30, 23, 59), is_dst=None)
print(((date2-date1) + DT.timedelta(minutes=1)).total_seconds()/3600.)
# 47.0

Do not use tzinfo=tzone 除非 tzonepytz.utc(或在其整个历史中始终相同的时区。)


日期1901-12-13 20:45:52 UTC从哪里来的?

您可以使用其 tzone._utc_transition_timestzone._transition_info 私有属性查看 pytz 时区的 utc 转换时间(和关联的转换信息):

In [43]: [(utcdate, utcoffset, dstoffset, tzabbrev) for utcdate, (utcoffset, dstoffset, tzabbrev) in zip(tzone._utc_transition_times, tzone._transition_info)][:2]
Out[43]: 
[(datetime.datetime(1, 1, 1, 0, 0),
  datetime.timedelta(0, 4560),
  datetime.timedelta(0),
  'LMT'),
 (datetime.datetime(1901, 12, 13, 20, 45, 52),
  datetime.timedelta(0, 3600),
  datetime.timedelta(0),
  'CET')]

这表明从日期 1-1-1 UTC1901-12-13 20:45:52 UTC,时区缩写为 LMT,utcoffset 为 4560 秒,等于 1 小时 16 分钟:

In [47]: print(DT.timedelta(0, 4560))
1:16:00

因此与 'Europe/Budapest' 关联的第一个时区是 LMT+1:16:00 STD