无毫秒的ISO格式打印日期时间

14

我正在尝试在API中序列化日期时间,但我不想要毫秒。我想要的格式在这里:https://en.wikipedia.org/wiki/ISO_8601 - "2015-09-14T17:51:31+00:00"

tz = pytz.timezone('Asia/Taipei')
dt = datetime.datetime.now()
loc_dt = tz.localize(dt)

尝试A:

loc_dt.isoformat()
>> '2015-09-17T10:46:15.767000+08:00'

尝试B:

loc_dt.strftime("%Y-%m-%dT%H:%M:%S%z")
>> '2015-09-17T10:46:15+0800'

后者几乎完美,只是时区部分缺少冒号。如何在不进行字符串操作(删除毫秒或添加冒号)的情况下解决这个问题?


我更喜欢使用格式化字符串的解决方案,因为这是我可以轻松放入序列化器中的内容。但我在strftime格式化字符串页面上没有看到任何有用的信息。 - Csaba Toth
ISO8601允许秒级别的分数。在格式化字符串之前,请尝试删除分数。 - Panagiotis Kanavos
你的代码在夏令时转换期间可能会失败,建议使用loc_dt = datetime.now(tz)来代替。 - jfs
@J.F.Sebastian 发生了什么,它是如何失败的?有时我本地化的不是datetime.now(),而是一个现有的datetime对象。 - Csaba Toth
@CsabaToth: 它可能会对模糊的本地时间返回错误的时间。如果您想消除现有的本地时间歧义,那么您需要额外的信息,例如 [在观察夏令时的情况下将有序时间戳解析为本地时间(到UTC)](https://dev59.com/c4Tba4cB1Zd3GeqP0ATo)。 - jfs
2个回答

18

您可以将微秒替换为0,并使用isoformat:

import pytz
from datetime import datetime
tz = pytz.timezone('Asia/Taipei')
dt = datetime.now()
loc_dt = tz.localize(dt).replace(microsecond=0)
print loc_dt.isoformat()
2015-09-17T19:12:33+08:00

如果你想保持 loc_dt 不变,那么在输出时进行替换:

loc_dt = tz.localize(dt)
print loc_dt.replace(microsecond=0).isoformat()

如评论所述,您最好将tz传递给datetime.now函数:

 dt = datetime.now(tz)

原因在pep-0495中有讨论,你可能还希望添加一个assert语句来捕获替换时的任何错误:

 ssert loc_dt.resolution >= timedelta(microsecond=0)

3
哇,从解析器的角度来看,这很奇怪。如果由于某种原因微秒为0,则完全省略输出?如果分钟或秒为零,则不会被省略。 - Csaba Toth
2
解析:https://dev59.com/8HVC5IYBdhLWcg3w-mVO 真不可思议!!! - Csaba Toth
@J.F.Sebastian,.replace(second=0).isoformat() 如何用于去除毫秒? - Padraic Cunningham
@J.F.Sebastian,那么使用上述代码,loc_dt.replace(microsecond=0).isoformat()怎么会失败呢?在什么情况下loc_dt.resolution可能是纳秒并导致非零纳秒的结果? - Padraic Cunningham
(1) assert 表示除非存在 bug,否则永远不会发生 (2) bug 是 loc_dt 是某个 datetime 子类,例如 pandas.Timestamp 或其他类似类型,可能具有 loc_dt.resolution == nanosecond(存在纳秒分辨率的接口,人们希望在不丢失信息或方便性的情况下进行往返转换)(3) 如果 loc_dt.resolution < microsecond,那么 .replace(microsecond=0) 可能不足够,我需要解释吗? - jfs
显示剩余12条评论

1
自从Python 3.6版本以来,datetime.isoformat函数接受一个timespec关键字参数以选择精度。这个参数指定了输出中你想要包含的最小时间单位:
>>> loc_dt.isoformat()
'2022-10-21T19:59:59.991999+08:00'

>>> loc_dt.isoformat(timespec='seconds')
'2022-10-21T19:59:59+08:00'

>>> loc_dt.isoformat(timespec='milliseconds')
'2022-10-21T19:59:59.991+08:00'

注意时间被截断而不是四舍五入。
您也可以使用timespec来去除秒/分:
>>> loc_dt.isoformat(timespec='minutes')
'2022-10-21T19:59+08:00'

>>> loc_dt.isoformat(timespec='hours')
'2022-10-21T19+08:00'

这里假设您先运行了以下的设置脚本:
from datetime import datetime
import pytz

tz = pytz.timezone('Asia/Taipei')
dt = datetime.now()
loc_dt = tz.localize(dt)

请注意,这可以在没有时区的情况下运作:
>>> from datetime import datetime
>>> now = datetime.now()
>>> now.isoformat(timespec='minutes')
>>> '2022-10-21T19:59'

这对于Python 3.x非常有用。原始问题是针对2.7的,但现在已经EOL了一段时间。 - Csaba Toth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接