我正在将国家气象服务警报提要解析为Web应用程序。我想在达到到期时清除警报。我还想以他们所属的地理区域的本地时间格式显示到期时间。
警报覆盖整个美国,所以我认为最好的方法是存储和比较UTC时间戳的时间。到期时间以字符串形式到达Feed中: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
有人可以帮助我找到我的错误吗?我不确定这是否是夏令时问题?我遇到了一些让我相信它可能试图将我的日期和时间本地化的东西但是在这一点上我很难过。我希望以时区无关的方式进行所有这些计算/比较。
答案 0 :(得分:3)
问题是夏令时被应用了两次。
一个简单的例子:
>>> 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()
内。正如它在documentation中所说的那样:
这是localtime()的反函数。它的 参数是struct_time或完整的9元组(因为dst标志是 需要;使用-1作为dst标志(如果它是未知的)表示 本地时间的时间,而不是UTC。它返回一个浮点数 兼容时间()。如果输入值不能表示为 一个有效的时间,将引发OverflowError或ValueError(其中 取决于Python是否捕获了无效值 底层C库)。它可以生成的最早日期 时间取决于平台。
当您将时间元组传递给time.mktime()
时,它需要一个关于时间是否在夏令时的标记。正如您在上面所看到的那样,utctimetuple()
会返回一个标记为0
的标记的元组,正如它在documentation中所说的那样:
如果datetime实例d是天真的,这与d.timetuple()相同 除了tm_isdst被强制为0而不管d.dst() 回报。 DST在UTC时间内永远不会生效。
如果d知道,则通过减去将d标准化为UTC时间 d.utcoffset()和标准化时间的time.struct_time是 回。 tm_isdst被强制为0.注意结果是tm_year 如果d.year是MINYEAR或MAXYEAR,则成员可以是MINYEAR-1或MAXYEAR + 1 和UTC调整溢出超过一年的边界。
由于您已告知time.mktime()
您的时间不是DST,并且其工作是将所有时间转换为当地时间,并且目前是您所在地区的夏令时,会增加一小时< / strong>使其成为夏令时。因此结果。
虽然我没有这个帖子,但几天前我遇到了一种方法,可以在当地时间将时区感知的日期时间转换成天真的日期。对于您的应用程序而言,这可能比您当前正在做的更好(使用优秀的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
将'America / LosAngeles'替换为您自己的时区字符串,您可以在pytz.all_timezones
中找到它。
答案 1 :(得分:0)
datetime.fromtimestamp()
是从POSIX时间戳获取本地时间的正确方法。您的问题中的问题是您使用不正确的time.mktime()
将感知日期时间对象转换为POSIX时间戳。这是one of correct ways to do it:
expiration_utc_ts = (d - datetime(1970, 1, 1, tzinfo=utc)).total_seconds()
local_dt = datetime.fromtimestamp(expiration_utc_ts)