datetime.now() 默认返回本地时区的 naive 时间,因依赖系统 TZ 环境变量,易在 Docker/CI 中出错;需显式指定时区或用 timezone.utc 获取 UTC 时间。

datetime.now() 为什么总返回本地时间,不是 UTC?
因为 datetime.now() 默认不带时区信息(naive),它直接用系统本地时区构造对象,但这个“本地”是运行时决定的,不是代码里写的。Docker 容器、CI 环境、不同服务器的 TZ 环境变量可能为空或设为 UTC,导致同一行代码在本地跑对、上线就错。
- 永远别依赖
datetime.now()的隐式行为;需要本地时间就显式传tz=zoneinfo.ZoneInfo("Asia/Shanghai") - 需要 UTC 时间,必须用
datetime.now(timezone.utc)(注意是timezone.utc,不是字符串"UTC") -
zoneinfo是 Python 3.9+ 标准库,旧版本得装backports.zoneinfo,且要确认系统时区数据库(tzdata)存在,否则ZoneInfo("Europe/London")会抛ZoneInfoNotFoundError
astimezone() 报错 “AttributeError: 'datetime.datetime' object has no attribute 'astimezone'”
这是典型的 naive datetime 调用 astimezone() 导致的——只有带时区信息(aware)的 datetime 才能转时区。而 datetime.strptime()、datetime.fromisoformat()(不带 Z 或时区偏移时)默认都生成 naive 对象。
- 检查
dt.tzinfo is None,为True就不能直接.astimezone() - 给 naive 时间“打上”时区用
.replace(tzinfo=...),但仅限你**确定它原本代表哪个时区**(比如日志里写“2024-05-10 14:00”,且明确是北京时间) - 更安全的做法是先转成 UTC:用
pytz.timezone("Asia/Shanghai").localize(dt)(pytz)或dt.replace(tzinfo=ZoneInfo("Asia/Shanghai")).astimezone(ZoneInfo("UTC"))(zoneinfo)
PyTZ 和 zoneinfo 混用导致时区偏移错误
pytz 的 timezone("Asia/Shanghai") 返回的是一个“时区对象”,而 zoneinfo.ZoneInfo("Asia/Shanghai") 是标准时区类型,二者不能互换。混用会导致 .astimezone() 计算出错,比如把 +08:00 算成 +09:00。
- 不要用 pytz 对象给
datetime.replace(tzinfo=...),会静默失败或偏移异常 - 旧项目用 pytz,升级到 zoneinfo 时,把所有
pytz.timezone(...)替换为ZoneInfo(...),并删掉pytz.utc改用timezone.utc -
datetime.utcnow()已废弃,它返回 naive UTC 时间,跟datetime.now(timezone.utc)有本质区别
strftime("%z") 输出空字符串或意外偏移
%z 只对 aware datetime 生效,且输出格式依赖底层 C 库。在 Alpine Linux(Docker 默认基础镜像)中,若没装 tzdata 包,ZoneInfo("Asia/Shanghai") 会 fallback 到 UTC,%z 就变成 +0000,而不是预期的 +0800。
立即学习“Python免费学习笔记(深入)”;
- 上线前务必验证:在目标环境运行
python -c "from zoneinfo import ZoneInfo; print(ZoneInfo('Asia/Shanghai').utcoffset(None))" - Dockerfile 中加
RUN apk add --no-cache tzdata(Alpine)或apt-get install -y tzdata(Debian) - 避免用
%Z(时区缩写),它不可靠(比如 CST 可能指 China Standard Time 或 Central Standard Time)










