HOWTO · Python

在 Python 中将 datetime 转换为带毫秒的字符串

将 Python datetime 转换为正好包含三位毫秒数字的 ISO 风格或自定义字符串。

本页内容

当 ISO 风格结果合适时,使用 value.isoformat(timespec="milliseconds")。当需要自定义布局时,使用带 %f 的 strftime() 并去掉最后三位。两种方法都会把微秒截断为毫秒,而不是四舍五入。

格式化只改变表示形式,不改变 datetime 值本身。示例使用固定输入,因此可以精确验证输出;在选择所需布局后,把这些构造器替换为应用中的值。如果接收系统需要偏移量,请在格式化前让该值带有时区。

使用 isoformat() 获得三位毫秒精度

datetime.isoformat() 是生成 ISO 8601 风格字符串的最清晰选择。timespec="milliseconds" 参数始终输出正好三位秒的小数部分。它可用于 Python 3.6 及更高版本。

请准确传入小写复数字符串 "milliseconds"。其他受支持的 timespec 值会选择不同精度,而未知值会引发 ValueError。当下游模式需要稳定字段宽度时,这个显式参数比依赖默认 "auto" 行为更合适。

下面的确定性示例使用带时区的 datetime,因此结果也说明 isoformat() 会保留 UTC 偏移量:

"""Show the recommended ISO-style millisecond formatting behavior."""

from datetime import datetime, timezone


value = datetime(2026, 9, 16, 12, 34, 56, 789654, tzinfo=timezone.utc)
print(value.isoformat(timespec="milliseconds"))

输出:

2026-09-16T12:34:56.789+00:00

六位 microsecond 值 789654 会变成 .789。Python 不会把该值四舍五入为 .790;timespec 控制显示哪些组件,被排除的时间组件会被截断。

当字符串必须跨系统标识一个真实时刻时,请使用带时区的 datetime。naive 值没有 UTC 偏移量,因此它的格式化文本本身无法区分本地时间、UTC 或其他时区。

isoformat() 会保留对象上已有的偏移量;它不会把值转换为 UTC。如果数据契约要求 UTC,请先规范化 datetime。反过来,只有在消费者明确期望本地壁钟时间时,才省略时区转换。

使用 strftime() 创建自定义布局

当需要控制顺序、分隔符或其他字段时,使用 strftime()。%f 指令会生成六位微秒数字。使用 [:-3] 切片会有意删除最后三位并保留毫秒精度:

"""Show millisecond precision in a custom datetime string format."""

from datetime import datetime


value = datetime(2026, 9, 16, 12, 34, 56, 789654)
print(value.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3])

输出:

2026-09-16 12:34:56.789

这种切片是安全的,因为 %f 总是提供一个零填充的六位字段,即使原始值没有微秒也是如此。在使用 [:-3] 前,请让 .%f 位于格式末尾;否则切片可能会删除其他字段中的字符。

该操作是截断而不是四舍五入。例如,789999 微秒仍会变成 789 毫秒。如果规范要求四舍五入,请在格式化前对 datetime 进行舍入,并处理可能进位到下一秒的情况;简单切片 %f 无法实现该规则。

导入方式决定如何调用这个类。使用 from datetime import datetime 时,调用 datetime.now()。如果改写为 import datetime,则调用 datetime.datetime.now()。

理解 str() 和切片边界

普通 str(value) 等同于 value.isoformat(" ")。其默认 timespec="auto" 会在 microsecond 为零时省略小数字段,否则输出全部六位微秒数字。因此,str(value) 不保证三位毫秒字段,盲目应用 [:-3] 并不安全。

这种可变宽度行为适合非正式显示,但不适合固定宽度字段。切片前检查小数点可以避免破坏秒数,但它仍会重复 isoformat(timespec="milliseconds") 已经处理的逻辑,并且还要额外注意时区后缀。

这个边界示例还说明,毫秒输出会在 999999 微秒处被截断,并且单数值 "millisecond" 无效:

"""Expose truncation, zero-microsecond slicing, and invalid-timespec boundaries."""

from datetime import datetime


almost_next_second = datetime(2026, 9, 16, 12, 34, 56, 999999)
without_fraction = datetime(2026, 9, 16, 12, 34, 56)

print(almost_next_second.isoformat(timespec="milliseconds"))
print(str(without_fraction))
print(str(without_fraction)[:-3])

try:
    without_fraction.isoformat(timespec="millisecond")
except ValueError as error:
    print(f"{type(error).__name__}: {error}")

输出:

2026-09-16T12:34:56.999
2026-09-16 12:34:56
2026-09-16 12:34
ValueError: Unknown timespec value

第三行不是有效的毫秒转换:它删除了 :56,因为输入没有小数字段。请优先使用 isoformat(timespec="milliseconds"),或者使用包含 %f 的格式,而不是切片 str() 的可变长度结果。

选择合适的方法

对于机器可读时间戳和标准交换,使用 isoformat(timespec="milliseconds")。它无需手动字符串处理,就能处理零微秒、截断和时区偏移。只有在消费者需要自定义布局时,才使用 strftime() 加 [:-3]。

请根据输出契约而不是方便程度来选择:isoformat() 提供标准化的日期时间形状,strftime() 提供调用方定义的形状,str() 提供适合人类阅读但小数精度可变的默认值。无论哪种情况,都要说明缺少偏移量表示本地时间、约定的 UTC,还是未知时区。

这些方法格式化已有的 datetime;它们不会把它转换为 Unix epoch 以来的毫秒,也不会把字符串解析为 datetime。还要记住,显示的三位数字描述的是毫秒精度,不一定代表时钟或存储源值的准确度。