两个 timedelta 对象与时区的差异

Rol*_*olf 4 python timezone datetime timedelta tzinfo

我想计算一个日期间隔有多少小时:例如,由于夏令时,“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)
Run Code Online (Sandbox Code Playgroud)

但是,它给出“2 天,0:00:00”,这是不正确的。我怎样才能制作一个考虑时区和夏令时的 timedelta 对象?另外,如果有一个更简单的解决方案来解决整个问题,我对此持开放态度。

谢谢你!

unu*_*tbu 5

在 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方法'Europe/Budapest',那么 pytz 将选择适合给定原始日期时间的时区(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>)
Run Code Online (Sandbox Code Playgroud)

相反,如果您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>)
Run Code Online (Sandbox Code Playgroud)

然后错误地选择关联的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
Run Code Online (Sandbox Code Playgroud)

不要使用tzinfo=tzoneexcept tzoneis pytz.utc(或在其历史中始终相同的时区。)


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

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

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')]
Run Code Online (Sandbox Code Playgroud)

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

In [47]: print(DT.timedelta(0, 4560))
1:16:00
Run Code Online (Sandbox Code Playgroud)

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

  • `is_dst=None` 告诉 `tzone.localize` 在夏令时结束时针对不明确的时间引发一个 `AmbigouslyTimeError` ——“回退”会导致相同的天真时间出现两次。否则它会做适当的事情。使用它是出于“非常谨慎”,作为一种良好的一般习惯,尽管它在这里没有任何效果。默认情况下,“is_dst=False”,这意味着“tzone.localize”将通过“假设”天真的日期时间指的是夏令时未生效时的日期时间来解决不明确的时间。 (2认同)