Python: strftime() 在Windows中UTC偏移不按预期工作
每次我使用:
time.strftime("%z")
我得到:
Eastern Daylight Time
不过,我想要的UTC时区偏移量格式是+HHMM或-HHMM。我甚至尝试过:
time.strftime("%Z")
但结果还是:
Eastern Daylight Time
我看过好几个关于strftime()的帖子,%z似乎总是能返回正确的+HHMM或-HHMM格式的UTC偏移量。请问我该如何让strftime()在Python 3.3中输出+HHMM或-HHMM格式呢?
补充:我在使用Windows 7。
5 个回答
我来这里是因为我需要一个解决方案,想要输出完整的日期和时间,包括时区,而且格式要符合RFC3339标准,具体可以查看这个链接 https://datatracker.ietf.org/doc/html/rfc3339 (是不是有点挑剔呢?) :)
看起来,strftime这个函数输出的内容并不符合这个特定的标准,所以最后我使用了:
from datetime import datetime, timezone
print(datetime.now(timezone.utc).astimezone().isoformat())
这个代码输出了以下内容:
2024-03-06T10:43:00.348657+00:00
我知道这并不是对问题的直接回答,但我觉得把它放在这里挺合适的,以防有人也像我一样走进这个迷宫 :)
这个问题在最新的Windows版本,也就是Win 10版本1703(创作者更新)中依然存在,大家应该早有耳闻。不过,时间总是向前走,现在有一个很棒的日期和时间库,叫做pendulum,它可以解决这个问题。这个库的主要作者是Sébatien Eustace,他向我展示过这个库的功能。
>>> pendulum.now().strftime('%z')
'-0400'
pendulum默认使用UTC/GMT时间,除非你特别说明其他时间。它还会把时区信息和日期时间对象一起保存。除此之外,还有很多其他的功能,下面列出了一些:
>>> pendulum.now(tz='Europe/Paris').strftime('%z')
'+0200'
>>> pendulum.create(year=2016, month=11, day=5, hour=16, minute=23, tz='America/Winnipeg').strftime('%z')
'-0500'
>>> pendulum.now(tz='America/Winnipeg').strftime('%z')
'-0500'
使用 time.timezone
可以获取时间偏移量,单位是秒。
然后可以用下面的方式格式化:
("-" if time.timezone > 0 else "+") + time.strftime("%H:%M", time.gmtime(abs(time.timezone)))
这样就能把它转换成 +/-HH:MM 的格式。
顺便问一下,这不是应该算是个bug吗?根据 strftime
的 文档。
我还以为 这个 StackOverflow 的回答能帮你把时区偏移字符串转换成 HH:MM 格式。不过因为 "%z"
没有按预期工作,我觉得这个问题就没什么意义了。
注意:time.timezone
不受夏令时的影响。
在2.x版本中,如果你查看time.strftime
的文档,你会发现根本没有提到%z
。这意味着它不一定存在,更别提在不同平台上的一致性了。实际上,正如脚注1所暗示的,这个功能是交给C语言的strftime
函数来处理的。而在3.x版本中,他们确实提到了%z
,不过解释它不按你预期工作的脚注并不容易找到;这就是一个未解决的bug。
然而,在2.6及以上版本(包括所有3.x版本)中,datetime.strftime
是可以保证支持%z
的,表示“以+HHMM或-HHMM的形式表示的UTC偏移(如果对象是简单的,则为空字符串)”。所以,这里有一个简单的解决办法:用datetime
代替time
。具体怎么改要看你想做什么——如果你使用Python-dateutil的tz
,那么datetime.now(tz.tzlocal()).strftime('%z')
就是获取本地时区格式化为GMT偏移的方式,但如果你想格式化一个完整的时间,细节会稍有不同。
如果你查看源代码,time.strftime
基本上只是检查格式字符串中是否有适合该平台的标识符,然后调用本地的strftime
函数,而datetime.strftime
则对不同的标识符有很多特殊处理,包括%z
;特别是,它会在传递给strftime
之前,将%z
替换为格式化后的utcoffset
。自从2.7以来,这段代码经历了几次变化,甚至有一次彻底重组,但在3.5之前的版本中,这种差异基本上还是存在的。
要找到合适的解决方案,请查看 abarnert 的回答。
你可以使用 time.altzone
,它会返回一个负的时间偏移量(以秒为单位)。比如说,我现在在中欧夏令时(CEST,UTC+2),所以我得到的是:
>>> time.altzone
-7200
如果你想把它放成你想要的格式,可以这样做:
>>> '{}{:0>2}{:0>2}'.format('-' if time.altzone > 0 else '+', abs(time.altzone) // 3600, abs(time.altzone // 60) % 60)
'+0200'
正如 abarnert 在评论中提到的,time.altzone
在夏令时(DST)生效时给出偏移量,而 time.timezone
则是在夏令时不生效时给出偏移量。要确定使用哪个,你可以按照 J.F. Sebastian 在他对另一个问题的回答中建议的方法来做 。 这样你就可以得到正确的偏移量:
time.altzone if time.daylight and time.localtime().tm_isdst > 0 else time.timezone
他还建议你可以在 Python 3 中使用以下方法,通过 datetime.timezone 来获取你想要的格式:
>>> datetime.now(timezone.utc).astimezone().strftime('%z')
'+0200'