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

16

这只是关于pytz的另一篇文章。

有两个函数可将日期时间对象在两个时区之间转换。第二个函数适用于所有情况。第一个函数在两种情况(3和4)下会失败。类似的SO帖子没有这样的问题。基于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 -- 正常

>>> 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 -- 可行

>>> 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) 

从UTC转换为EST - 成功

>>> 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)从协调世界时到东部夏令时--可以

>>> 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) 从东部标准时间到协调世界时 -- 可以。

>>> 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 -- OK。

>>> 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>)

无法重现:>>> 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=<UTC>) - Mark Ransom
抱歉,忘记添加导入行
from dateutil.parser import parse import pytz
- Asif Rehan
你的问题要么是 pytz localize vs datetime replace 的重复,要么就应该询问一个更具体的问题(更新当前问题),如果你对现有答案不满意。请使用不同的名称来命名第一个和第二个函数,例如 convert_tz_replace()convert_tz() - jfs
@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=<UTC>),即.replace()无法正常工作,如文档所述。 - jfs
@J.F.Sebastian 那是我测试的直接复制/粘贴。我不知道为什么会得到不同的结果,但我确实得到了。也许这是某种线索。 - Mark Ransom
请参考 https://www.reddit.com/r/CodingHelp/comments/10638wm/pst_adjustment_goes_to_0753_why_is_my_timezone/,了解可能的解释。 - Barry Carter
1个回答

18

我假设您有以下几个问题:

  • 为什么第一个函数适用于UTC时区?
  • 为什么它对于“US / Eastern”时区(DSTTzInfo实例)失败了?
  • 为什么第二个函数在所有提供的示例中都有效?

第一个函数是错误的,因为它使用了而不是。

第二个函数大多数时间是正确的,除了在模糊或不存在的时间,例如在夏令时转换期间 - 您可以通过将is_dst参数传递给.localize()来更改行为:(默认)/ / (引发异常)。

第一个函数适用于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')返回一个DstTzInfo实例,其(_tzname, _utcoffset, _dst)属性对应于某个未记录的时刻(不同的pytz版本可能返回不同的值——当前版本可能返回与可用区域信息最早的日期相对应的tzinfo实例——大多数情况下您不想要这个值:我认为选择默认值的动机是强调错误(传递pytz.timezonedatetime构造函数或.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),其中 n 是具有不同 (utcoffset、tzname、dst) 值的间隔数量,但是datetime 构造函数和 .replace()仍然按原样工作,即显式的 localize() 调用只是在传递 is_dst 时才需要。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接