用 Python 标准库掌握时间处理:datetime 全面攻略

系列 03 – 日期/时间运算、时区、格式转换一次搞定

时间不只是“字符串”。日期会跨日、月份会变、夏令时会插入,且不同地区的基准也不同。 因此在处理日期/时间时,必须区分可显示的字符串可计算的时间值,并养成包含时区的习惯。

时钟工匠的魔法工作室

本文围绕 datetime 模块,整理以下内容。

  • 创建当前时间与日期
  • 计算时间差(timedelta
  • 字符串格式化/解析(strftimestrptime
  • 时区处理(timezonezoneinfo
  • 常见注意点与稳定模式

1. datetime 提供了哪些功能?



datetime 包含看似相似但职责不同的类型。

  • date:仅需要年-月-日
  • time:仅需要时:分:秒
  • datetime:日期+时间(最常用)
  • timedelta:时间“差值”(持续时间)
  • timezone:固定偏移时区(如 UTC+9,偏移不变)

Python 3.9+ 之后,标准库加入 zoneinfo,使得处理地区时区(如 Asia/Tokyo)更为简便。


2. 如何获取“现在”:从 naive 到 aware 的整理

2.1 naive / aware 是什么?

datetime 对象大致分为两类。

  • naive datetime:没有时区信息
  • aware datetime:包含时区信息(tzinfo

混用两者进行运算/比较会导致错误,甚至在无错误的情况下得到意外结果。

2.2 推荐的默认值:以 UTC aware 开始

统一使用 UTC 作为存储/计算基准往往更为整洁。

from datetime import datetime, timezone

utc_now = datetime.now(timezone.utc)  # aware (UTC)
print(utc_now)

需要本地时间时,在显示阶段进行转换。

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

utc_now = datetime.now(timezone.utc)
tokyo_now = utc_now.astimezone(ZoneInfo("Asia/Tokyo"))

print(utc_now)
print(tokyo_now)

3. 日期/时间计算:timedelta 为核心



3.1 加减

from datetime import datetime, timedelta, timezone

now = datetime.now(timezone.utc)
print(now + timedelta(days=3))
print(now - timedelta(hours=2))

3.2 两个时间点的差值

from datetime import datetime, timezone

a = datetime(2026, 1, 1, tzinfo=timezone.utc)
b = datetime(2026, 1, 10, tzinfo=timezone.utc)

delta = b - a
print(delta.days)            # 9
print(delta.total_seconds()) # 777600.0

3.3 用 timedelta 表示“期间”的感知

timedelta天/秒/微秒为单位。 “一个月后”这类因月长变化的表达,timedelta(days=30) 只能粗略近似;若需基于日历的“下个月同日”,则需要 dateutil 等外部工具。


4. 格式化与解析:strftime / strptime

4.1 datetime → 字符串(strftime

from datetime import datetime, timezone

dt = datetime(2026, 1, 30, 14, 5, 0, tzinfo=timezone.utc)
print(dt.strftime("%Y-%m-%d %H:%M:%S %z"))

常用格式模式:

  • %Y-%m-%d : 2026-01-30
  • %H:%M:%S : 14:05:00
  • %z : +0000(UTC 偏移)

4.2 字符串 → datetime(strptime

from datetime import datetime

s = "2026-01-30 14:05:00"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
print(dt)

此处得到的 dtnaive。若想明确时区,需要附加 tzinfo

from datetime import datetime, timezone

s = "2026-01-30 14:05:00"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc)
print(dt)

replace(tzinfo=...) 只是给时间点贴上时区标签,并不做时区转换。 若需转换,请使用 astimezone()(见下节)。


5. 时区:区分 timezonezoneinfo 的使用

5.1 固定偏移时区使用 timezone

示例:UTC、UTC+9 等偏移固定的时区。

from datetime import datetime, timezone, timedelta

kst_fixed = timezone(timedelta(hours=9))
dt = datetime(2026, 1, 30, 12, 0, tzinfo=kst_fixed)
print(dt)

5.2 地区时区使用 zoneinfo

夏令时等偏移变化的地区适合使用 zoneinfo

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

utc_now = datetime.now(timezone.utc)
ny_now = utc_now.astimezone(ZoneInfo("America/New_York"))
print(ny_now)

5.3 replace(tzinfo=...)astimezone(...) 的区别

  • replace(tzinfo=...)时间值不变,仅给时区贴标签
  • astimezone(...)同一时刻在不同时区的时间表示

了解这点可大幅减少时区相关错误。


6. 常见注意点汇总

6.1 naive/aware 混用

  • 内部计算时间建议使用 aware(UTC),更稳健。
  • 外部输入时,立即确定时区并统一。

6.2 “本地时间”受环境影响

datetime.now() 取决于执行环境的本地设置。容器/服务器可能是 UTC,也可能不是。 使用 datetime.now(timezone.utc) 更可靠。

6.3 字符串解析的格式不匹配

strptime 对格式极为严格。若输入格式多样,需先做预处理或逐步尝试多种格式(最好限制输入格式)。


7. 三大常用模式

7.1 以 ISO 8601 存储

标准字符串表示可用 isoformat()

from datetime import datetime, timezone

dt = datetime.now(timezone.utc)
print(dt.isoformat())  # 例如: 2026-01-30T05:12:34.567890+00:00

7.2 用“今天日期”生成文件名

from datetime import datetime

stamp = datetime.now().strftime("%Y%m%d")
filename = f"report_{stamp}.json"
print(filename)

7.3 计算到某个时间点的剩余时间

from datetime import datetime, timezone

target = datetime(2026, 2, 1, 0, 0, tzinfo=timezone.utc)
now = datetime.now(timezone.utc)

remaining = target - now
print(remaining)
print(remaining.total_seconds())

8. 结语

datetime 不仅能生成日期字符串,更能把时间变成可运算的值。结合时区(timezonezoneinfo),即使运行环境不同,也能写出稳定的代码。

下一篇将单独讨论 random,涵盖随机数生成/采样/洗牌/种子以及安全随机数(secrets)等。


相关文章:


查看前置系列