用Python将时区识别日期字符串转换为UTC并返回



我正在将国家气象局的警报源解析到一个web应用程序中。我想在警报达到到期时间时清除它们。我还想以本地时间格式显示它们所属地理区域的到期时间。

警报覆盖了整个美国,所以我认为最好的方法是以UTC时间戳存储和比较时间。到期时间以如下字符串的形式出现在提要中:2011-09-09T22:12:00-04:00

我使用Labix dateutils包以时区感知的方式解析字符串:

>>> from dateutil.parser import parse
>>> d = parse("2011-09-18T15:52:00-04:00")
>>> d
datetime.datetime(2011, 9, 18, 15, 52, tzinfo=tzoffset(None, -14400))

我还可以捕捉UTC偏移量(以小时为单位(:

>>> offset_hours = (d.utcoffset().days * 86400 + d.utcoffset().seconds) / 3600
>>> offset_hours
-4

使用datetime.utctimetuple()time.mktime()方法,我能够将解析的日期转换为UTC时间戳:

>>> import time
>>> expiration_utc_ts = time.mktime(d.utctimetuple())
>>> expiration_utc_ts
1316393520.0

在这一点上,我感觉很好,我能够将原始字符串转换为以UTC表示过期时间的时间戳。我可以将当前时间作为UTC时间戳与过期时间进行比较,并确定是否需要清除:

>>> now_utc_ts = time.mktime(time.gmtime())
>>> now_utc_ts
1316398744.0
>>> now_utc_ts >= expiration_tc_ts
True

我遇到的困难是尝试将存储的UTC时间戳转换回原始本地化格式。我从原始转换中存储了偏移小时数,并解析了一个字符串来存储时区标签:

>>> print offset_hours
-4
>>> print timezone
EDT

我想将UTC时间戳转换回本地格式化的时间,但将其转换回datetime似乎不起作用:

>>> import datetime
>>> datetime.datetime.fromtimestamp(expiration_utc_ts) + datetime.timedelta(hours=offset_hours)
datetime.datetime(2011, 9, 18, 16, 52) # The hour is 16 but it should be 15

看起来要休息一个小时。我不确定错误是从哪里引入的?我做了另一个测试,得到了类似的结果:

>>> # Running this at 21:29pm EDT
>>> utc_now = datetime.datetime.utcnow()
>>> utc_now_ts = time.mktime(right_now.utctimetuple())
>>> datetime.datetime.fromtimestamp(utc_now_ts)
datetime.datetime(2011, 9, 18, 22, 29, 47) # Off by 1 hour

有人能帮我找出错误吗?我不确定这是不是夏令时的问题?我发现了一些东西,让我相信它可能试图本地化我的日期和时间,但在这一点上,我很困惑。我希望以一种与时区无关的方式进行所有这些计算/比较。

问题是夏令时被应用了两次。

一个微不足道的例子:

>>> time_tuple = datetime(2011,3,13,2,1,1).utctimetuple()
time.struct_time(tm_year=2011, tm_mon=3, tm_mday=13, tm_hour=2, tm_min=1, tm_sec=1, tm_wday=6, tm_yday=72, tm_isdst=0)
>>> datetime.fromtimestamp(time.mktime(time_tuple))
datetime.datetime(2011, 3, 13, 3, 1, 1)

我相当确定故障在time.mktime()内。正如它在文件中所说:

这是localtime((的反函数。它的自变量是struct_time或完整的9元组(因为dst标志为需要;如果未知,则使用-1作为dst标志(,表示当地时间,而不是UTC。它返回一个浮点数,用于与时间的兼容性((。如果输入值不能表示为有效时间,OverflowError或ValueError将被引发(取决于无效值是被Python捕获还是被底层C库(。它可以生成的最早日期时间取决于平台。

当您将时间元组传递给time.mktime()时,它需要一个关于该时间是否为夏令时的标志。正如您在上面看到的,utctimetuple()返回一个带有标记为0的标志的元组,正如它在文档中所说的那样:

如果日期时间实例d是幼稚的,这与d.timetuple((相同除了tm_isdst被强制为0,而不管d.dst((是什么退货。夏令时在UTC时间内从未生效。

如果d知道,则通过减去d.utoffset((,标准化时间的time.struct_time为返回。tm_isdst被强制为0。请注意,结果为tm_year如果d.year是MINYEAR或MAXYEAR,则成员可以是MINYEAR-1或MAXYEAR+1UTC调整超出了一年的界限。

由于您已经告诉time.mktime()您的时间不是夏令时,它的工作是将所有时间转换为当地时间,并且当前您所在地区的时间是夏令时,因此它添加了一个小时使其成为夏令时。结果就是这样。


虽然我手头没有这篇文章,但几天前我发现了一种方法,可以将时区感知的日期时间转换为当地时间的天真时间。这可能比你目前正在做的(使用出色的pytz模块(更适合你的应用程序:

import pytz
def convert_to_local_time(dt_aware):
    tz = pytz.timezone('America/Los_Angeles') # Replace this with your time zone string
    dt_my_tz = dt_aware.astimezone(tz)
    dt_naive = dt_my_tz.replace(tzinfo=None)
    return dt_naive

将"美国/洛杉矶"替换为您自己的时区字符串,您可以在pytz.all_timezones中找到该字符串。

datetime.fromtimestamp()是从POSIX时间戳获取本地时间的正确方法。问题中的问题是,您使用time.mktime()将感知日期时间对象转换为POSIX时间戳,这是不正确的。以下是正确的方法之一:

expiration_utc_ts = (d - datetime(1970, 1, 1, tzinfo=utc)).total_seconds()
local_dt = datetime.fromtimestamp(expiration_utc_ts)

相关内容

  • 没有找到相关文章

最新更新