使用pytz进行日期时间时区转换 [英] Datetime Timezone conversion using pytz

查看:29
本文介绍了使用pytz进行日期时间时区转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

<预><代码>>>>从 dateutil.parser 导入解析>>>进口pytz

第一个函数(buggy)

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

def buggy_timezone_converter(input_dt, current_tz='UTC', target_tz='US/Eastern'):'''input_dt 是一个 datetime.datetime 对象'''current_tz = pytz.timezone(current_tz)target_tz = pytz.timezone(target_tz)target_dt = input_dt.replace(tzinfo=current_tz).astimezone(target_tz)返回 target_tz.normalize(target_dt)

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

(1) 从 UTC 到 EST -- 好的

<预><代码>>>>buggy_timezone_converter(解析('2013-02-26T04:00:00'))Out[608]: datetime.datetime(2013, 2, 25, 23, 0, tzinfo=)

(2) 从 UTC 到 EDT -- OK

<预><代码>>>>buggy_timezone_converter(解析('2013-05-26T04:00:00'))Out[609]: datetime.datetime(2013, 5, 26, 0, 0, tzinfo=)

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

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

第二个功能(完美运行)

下面的函数使用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)返回 target_tz.normalize(target_dt)

(1) 从 UTC 到 EST -- 好的

<预><代码>>>>good_timezone_converter(解析('2013-02-26T04:00:00'))Out[618]: datetime.datetime(2013, 2, 25, 23, 0, tzinfo=)

(2) 从 UTC 到 EDT -- OK

<预><代码>>>>good_timezone_converter(解析('2013-05-26T04:00:00'))Out[619]: datetime.datetime(2013, 5, 26, 0, 0, tzinfo=)

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

(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 时区?
  • 为什么 'US/Eastern' 时区(DstTzInfo 实例)失败?
  • 为什么第二个函数适用于所有提供的示例?

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

第二个函数大部分时间都是正确的,除了在不明确或不存在的时间,例如在 DST 转换期间——您可以通过将 is_dst 参数传递给 .localize(): False(default)/True/None(引发异常).

第一个函数适用于 UTC 时区,因为它对任何日期都有固定的 UTC 偏移量(零).其他时区,例如 America/New_York 可能在不同时间(夏令时、战争时间、某些当地政治家可能认为是个好主意的任何时间 - 它可以是 任何东西——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) 属性对应于某些 undocumented 时间(不同的 pytz 版本可能返回不同的值——当前版本可能返回对应于 zoneinfo 的最早日期的 tzinfo 实例可用 - 大多数时候你不想要这个值:我认为选择默认值背后的动机是突出错误(将 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) 在 n是具有不同 (utcoffset, tzname, dst) 值的间隔数,但 datetime 构造函数和 .replace() 将按原样工作,即显式 localize() 调用只需要传递 is_dst.

This is just another post on pytz.

There are two functions to convert datetime objects between two timezones. The second functions works for all cases. The first function fails in two cases, (3) and (4). Similar SO post did not have an issue like this. Any explanation based on the difference between localize(datetime.datetime) and replace(tzinfo) would be a great help.

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

First function (buggy)

The function below uses 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)

Notice the four datetime conversion now.

(1) from UTC to 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) from UTC to 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) from EST to UTC -- Not OK. Time offset is 4 hours 56 minutes. It is supposed to be 5 hours

>>> 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) from EDT to UTC -- Not OK. Time offset is 4 hours 56 minutes. It is supposed to be 4 hours. Daylight saving is not considered.

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

Second function (Works perfectly)

The function below uses pytz.timezone.localize(datetime.datetime). It works perfectly

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) from UTC to 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) from UTC to 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) from EST to UTC -- OK.

>>> 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) from EDT to 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>)

解决方案

I assume you have these questions:

  • why does the first function work for UTC timezone?
  • why does it fail for 'US/Eastern' timezone (DstTzInfo instance)?
  • why does the second function work for all provided examples?

The first function is incorrect because it uses d.replace(tzinfo=dsttzinfo_instance) instead of dsttzinfo_instance.localize(d) .

The second function is correct most of the time except during ambiguous or non-existing times e.g., during DST transitions -- you can change the behaviour by passing is_dst parameter to .localize(): False(default)/True/None(raise an exception).

The first function works for UTC timezone because it has a fixed utc offset (zero) for any date. Other timezones such as America/New_York may have different utc offsets at different times (Daylight saving time, war time, any time that some local politician might think is a good idea -- it can be anything -- the tz database works in most cases). To implement tzinfo.utcoffset(dt), tzinfo.tzname(dt), tzinfo.dst(dt) methods pytz uses a collection of DstTzInfo instances each with a different set of (_tzname, _utcoffset, _dst) attributes. Given dt (date/time) and is_dst, .localize() method chooses an appropriate (in most cases but not always) DstTzInfo instance from the collection. pytz.timezone('America/New_York') returns a DstTzInfo instance with (_tzname, _utcoffset, _dst) attributes that correspond to some undocumented moment in time (different pytz versions may return different values -- the current version may return tzinfo instance that corresponds to the earliest date for which zoneinfo is available -- you don't want this value most of the time: I think the motivation behind the choice of the default value is to highlight the error (passing pytz.timezone to datetime constructor or .replace() method).

To summarize: .localize() selects appropriate utcoffset, tzname, dst values, .replace() uses the default (inappropriate) value. UTC has only one set of utcoffset, tzname, dst therefore the default value may be used and .replace() method works with UTC timezone. You need to pass a datetime object and is_dst parameter to select appropriate values for other timezones such as 'America/New_York'.

In principle, pytz could have called localize() method to implement utcoffset(), tzname(), dst() methods even if dt.tzinfo == self: it would make these methods O(log n) in time where n is number of intervals with different (utcoffset, tzname, dst) values but datetime constructor and .replace() would work as is i.e., the explicit localize() call would be necessary only to pass is_dst.

这篇关于使用pytz进行日期时间时区转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆