python 3.9+ 推荐用 zoneinfo 替代 pytz,通过 zoneinfo 显式绑定时区、避免 naive datetime,解析字符串后手动设时区,存储统一用 utc,展示层再转换。

Python 处理时区的核心是正确使用 datetime 与 zoneinfo(Python 3.9+)或 pytz(旧版本),避免用 naive datetime 表示带时区的时间。
用 zoneinfo 替代 pytz(推荐,Python 3.9+)
zoneinfo 是 Python 标准库内置模块,基于 IANA 时区数据库,API 更简洁、行为更符合直觉,不再需要 localize() 和 astimezone() 的复杂区分。
- 获取带时区的当前时间:
from zoneinfo import ZoneInfo
from datetime import datetime
dt = datetime.now(ZoneInfo("Asia/Shanghai")) - 将 naive datetime 转为指定时区(视为该时区本地时间):
naive = datetime(2024, 5, 1, 12, 0)
shanghai = naive.replace(tzinfo=ZoneInfo("Asia/Shanghai")) - 转换时区(自动处理夏令时和历史偏移):
utc_time = shanghai.astimezone(ZoneInfo("UTC"))
避免 naive datetime 参与时区运算
naive datetime(无 tzinfo)不代表任何具体时刻,直接调用 astimezone() 会触发隐式本地时区解释,结果不可靠且平台依赖。
- 错误写法:
dt = datetime(2024, 5, 1, 12, 0) # naive
dt.astimezone(ZoneInfo("UTC")) # ❌ 隐式转成本地时区再转 UTC,易出错 - 正确做法:先明确其所属时区,再转换:
dt = datetime(2024, 5, 1, 12, 0, tzinfo=ZoneInfo("Asia/Shanghai"))
dt.astimezone(ZoneInfo("UTC")) # ✅ 明确语义
读写时间字符串时显式指定时区
用 datetime.fromisoformat() 或 strptime() 解析字符串时,若含时区信息(如 "2024-05-01T12:00:00+08:00"),会自动返回 aware datetime;但常见格式如 "2024-05-01 12:00:00" 默认生成 naive 对象。
立即学习“Python免费学习笔记(深入)”;
- 解析带偏移的 ISO 字符串(支持):
datetime.fromisoformat("2024-05-01T12:00:00+08:00") # 返回 aware - 解析无时区字符串后手动绑定(推荐):
s = "2024-05-01 12:00:00"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S").replace(tzinfo=ZoneInfo("Asia/Shanghai")) - 序列化时强制输出时区信息:
dt.isoformat() # 如 "2024-05-01T12:00:00+08:00"
跨系统/存储场景统一用 UTC 存储
数据库、日志、API 响应等持久化或传输环节,应始终保存 UTC 时间(aware + ZoneInfo("UTC")),展示层再按需转本地时区。这规避了夏令时切换、多地区用户、服务器时区不一致等问题。
- 入库前转 UTC:
user_time = datetime.now(ZoneInfo("Asia/Shanghai"))
utc_time = user_time.astimezone(ZoneInfo("UTC")) - 前端或报表中转回用户所在时区:
utc_time.astimezone(ZoneInfo(user_tz)) - Django/Flask 等框架通常默认启用 timezone-aware 模式,注意配置
TIME_ZONE = 'UTC'并开启USE_TZ = True










