使用 pytz 进行日期时间时区转换

Posted

技术标签:

【中文标题】使用 pytz 进行日期时间时区转换【英文标题】:Datetime Timezone conversion using pytz 【发布时间】:2015-02-16 08:42:50 【问题描述】:

这只是pytz 上的另一个帖子。

有两个函数可以在两个时区之间转换日期时间对象。第二个功能适用于所有情况。第一个函数在两种情况下失败,(3)和(4)。类似的SO post 没有这样的问题。任何基于localize(datetime.datetime)replace(tzinfo) 之间差异的解释都会有很大帮助。

>>> from dateutil.parser import parse
>>> import pytz

第一个功能(越野车)

下面的函数使用datetime.datetime.replace(tzinfo)

def buggy_timezone_converter(input_dt, current_tz='UTC', target_tz='US/Eastern'):
    '''input_dt is a datetime.datetime object'''
    current_tz = pytz.timezone(current_tz)
    target_tz = pytz.timezone(target_tz)
    target_dt = input_dt.replace(tzinfo=current_tz).astimezone(target_tz)
    return target_tz.normalize(target_dt)

现在注意四个日期时间转换。

(1) 从 UTC 到 EST -- OK

>>> buggy_timezone_converter(parse('2013-02-26T04:00:00'))
Out[608]: datetime.datetime(2013, 2, 25, 23, 0, tzinfo=<DstTzInfo 'US/Eastern' EST-1 day, 19:00:00 STD>)

(2) 从 UTC 到 EDT -- OK

>>> buggy_timezone_converter(parse('2013-05-26T04:00:00'))
Out[609]: datetime.datetime(2013, 5, 26, 0, 0, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)

(3) 从 EST 到 UTC - 不正确。时间偏移为 4 小时 56 分钟。应该是5小时

>>> buggy_timezone_converter(parse('2013-02-26T04:00:00'), target_tz='UTC', current_tz='US/Eastern')
Out[610]: datetime.datetime(2013, 2, 26, 8, 56, tzinfo=<UTC>)

(4) 从 EDT 到 UTC——不正常。时间偏移为 4 小时 56 分钟。应该是4小时。不考虑夏令时。

>>> buggy_timezone_converter(parse('2013-05-26T04:00:00'), current_tz='US/Eastern', target_tz='UTC')
Out[611]: datetime.datetime(2013, 5, 26, 8, 56, tzinfo=<UTC>)

第二个功能(完美运行)

下面的函数使用pytz.timezone.localize(datetime.datetime)。效果很好

def good_timezone_converter(input_dt, current_tz='UTC', target_tz='US/Eastern'):
    current_tz = pytz.timezone(current_tz)
    target_tz = pytz.timezone(target_tz)
    target_dt = current_tz.localize(input_dt).astimezone(target_tz)
    return target_tz.normalize(target_dt) 

(1) 从 UTC 到 EST -- OK

>>> good_timezone_converter(parse('2013-02-26T04:00:00'))
Out[618]: datetime.datetime(2013, 2, 25, 23, 0, tzinfo=<DstTzInfo 'US/Eastern' EST-1 day, 19:00:00 STD>)

(2) 从 UTC 到 EDT -- OK

>>> good_timezone_converter(parse('2013-05-26T04:00:00'))
Out[619]: datetime.datetime(2013, 5, 26, 0, 0, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)

(3) 从 EST 到 UTC -- 好的。

>>> good_timezone_converter(parse('2013-02-26T04:00:00'), current_tz='US/Eastern', target_tz='UTC')
Out[621]: datetime.datetime(2013, 2, 26, 9, 0, tzinfo=<UTC>)

(4) 从 EDT 到 UTC -- 好的。

>>> good_timezone_converter(parse('2013-05-26T04:00:00'), current_tz='US/Eastern', target_tz='UTC')
Out[620]: datetime.datetime(2013, 5, 26, 8, 0, tzinfo=<UTC>)

【问题讨论】:

无法重现:&gt;&gt;&gt; timezone_converter(datetime.datetime(2013,02,26,4,0,0,0), target_tz='UTC', current_tz='US/Eastern')datetime.datetime(2013, 2, 26, 9, 0, tzinfo=&lt;UTC&gt;) 抱歉,忘记添加导入行 >>> from dateutil.parser import parse >>> import pytz 您的问题与pytz localize vs datetime replace 重复,或者如果您对现有答案不满意,您应该提出更具体的问题 (update the current question)。请为第一个和第二个函数使用不同的名称,例如 convert_tz_replace()convert_tz() @MarkRansom:再次检查(更新pytz)。 timezone_converter_replace(datetime(2013,2,26,4,0,0,0), target_tz='UTC', current_tz='US/Eastern') -> datetime.datetime(2013, 2, 26, 8, 56, tzinfo=&lt;UTC&gt;).replace() 失败,如记录。 @J.F.Sebastian 这是我测试的直接复制/粘贴。我不知道为什么我得到了不同的结果,但我做到了。也许这是某种线索。 【参考方案1】:

我假设您有以下问题:

为什么第一个函数适用于 UTC 时区? 为什么 'US/Eastern' 时区(DstTzInfo 实例)失败? 为什么第二个函数对所有提供的示例都有效?

第一个函数不正确,因为它使用d.replace(tzinfo=dsttzinfo_instance) 而不是dsttzinfo_instance.localize(d)

第二个函数在大多数情况下都是正确的,除了在不明确或不存在的时间(例如,在 DST 转换期间)——您可以通过将 is_dst 参数传递给 .localize() 来更改行为:False(默认)/ True/None(引发异常)。

第一个函数适用于 UTC 时区,因为它对于任何日期都有固定的 UTC 偏移量(零)。其他时区,例如 America/New_York 可能在不同时间有不同的 UTC 偏移量(夏令时、战争时间、一些当地政客可能认为是个好主意的任何时间——它可以是任何东西—— tz 数据库在大多数情况下都有效)。要实现tzinfo.utcoffset(dt)tzinfo.tzname(dt)tzinfo.dst(dt) 方法pytz 使用DstTzInfo 实例的集合,每个实例具有不同的(_tzname, _utcoffset, _dst) 属性集。给定dt(日期/时间)is_dst.localize() 方法会从集合中选择一个合适的(在大多数情况下,但并非总是DstTzInfo 实例. pytz.timezone('America/New_York') 返回一个带有(_tzname, _utcoffset, _dst) 属性的DstTzInfo 实例,这些属性对应于一些未记录 时刻(不同的pytz 版本可能返回不同的值——当前版本可能返回tzinfo 实例对应于 zoneinfo 可用的最早日期 - 大多数时候你不想要这个值:我认为选择默认值背后的动机是突出错误(将 pytz.timezone 传递给 datetime构造函数或.replace() 方法)。

总结一下:.localize() 选择合适的 utcoffset、tzname、dst 值,.replace() 使用默认(不合适的)值。 UTC 只有一组 utcoffset、tzname、dst,因此可以使用默认值,.replace() 方法适用于 UTC 时区。您需要传递一个日期时间对象和is_dst 参数来为其他时区选择适当的值,例如'America/New_York'

原则上,pytz 可以调用localize() 方法来实现utcoffset()tzname()dst() 方法,即使dt.tzinfo == self:它会使这些方法在时间为 O(log n) 其中@ 987654360@ 是具有不同 (utcoffset, tzname, dst) 值的间隔数,但 datetime 构造函数和 .replace() 将按原样工作,即显式 localize() 调用仅需要传递 is_dst

【讨论】:

以上是关于使用 pytz 进行日期时间时区转换的主要内容,如果未能解决你的问题,请参考以下文章

使用 pytz 进行日期时间和时区转换 - 令人兴奋的行为

使用 pytz 将日期时间从一个时区转换为另一个时区

python 使用`pytz`将时间和日期对话转换为其他时区。

Python pytz 时区转换返回的值与不同日期的时区偏移量不同

使用 pytz 时区时 Python 日期时间不包括 DST

使用pytz的python时区转换问题