关于python:datetime使用pytz进行时区转换

Datetime Timezone conversion using pytz

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

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

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

第一功能(Buggy)

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

1
2
3
4
5
6
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--好

1
2
>>> 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--好

1
2
>>> 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个小时

1
2
>>> 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个小时。不考虑日光节约。

1
2
>>> 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)。它工作得很好

1
2
3
4
5
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--好

1
2
>>> 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--好

1
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)从EST到UTC——好的。

1
2
>>> 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——好。

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


我假设你有以下问题:

  • 为什么第一个函数适用于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)属性。鉴于EDOCX1(日期/时间)和is_dst.localize()方法从集合中选择适当的(在大多数情况下,但不总是)DstTzInfo实例。pytz.timezone('America/New_York')返回一个DstTzInfo实例,其中(_tzname, _utcoffset, _dst)属性对应于某个未记录的时间点(不同的pytz版本可能返回不同的值当前版本可能返回tzinfo实例,该实例对应于ZoneInfo可用的最早日期您不需要这个值e多数情况下:我认为选择默认值的动机是为了突出错误(将pytz.timezone传递给datetime构造函数或.replace()方法)。

总结:.localize()选择合适的utcoffset、tzname、dst值,.replace()使用默认(不合适)值。UTC只有一组utcoffset、tzname和dst,因此可以使用默认值,并且.replace()方法与UTC时区一起工作。您需要传递datetime对象和is_dst参数,以便为其他时区(如'America/New_York'选择适当的值。

原则上,pytz可以调用localize()方法来实现utcoffset()tzname()dst()方法,即使dt.tzinfo == self也可以:如果n是具有不同(utcoffset、tzname、dst)值但datetime构造函数和.replace()wo值的间隔数,那么这些方法会及时地变为o(logn),而n是具有不同(utcoffset、tzname、dst)值的间隔数。ULD按原样工作,即只需要显式的localize()调用来传递is_dst