1. 什么是 tzdata? #
- tzdata 是 Python 的时区数据包(当前版本 2025.x),提供 IANA 全球时区规则。
- 它不提供写时间的 API,而是给标准库
zoneinfo 提供时区数据。
- Windows、精简 Docker 等环境系统可能没有 IANA 数据,需
pip install tzdata,否则会报 ZoneInfoNotFoundError。
- 典型场景:UTC 转北京时间展示、Flask 模板格式化时间、日志对齐。
- 国内项目统一用
Asia/Shanghai(UTC+8,无夏令时)。
- 写代码用
zoneinfo.ZoneInfo;tzdata 是背后的数据,两者配合使用。
2. 前置知识 #
- 用时区前先弄清下面三个概念,避免把「本地时间」和「UTC」搞混。
2.1 UTC、naive 与 aware #
- UTC 是全球时间基准;服务器和数据库内部通常统一存 UTC。
- naive datetime:没有时区信息,如
datetime(2026, 5, 30, 12, 0)。
- aware datetime:带时区,如
2026-05-30 12:00:00+08:00。
- 跨时区转换前,必须先把 naive 时间标记为某个时区(变成 aware),再
.astimezone()。
2.2 IANA 时区名称 #
- 格式为
洲/城市,如 Asia/Shanghai。
- 不要用
CST 等缩写(含义不唯一)。
- 中国全境用
Asia/Shanghai,没有 Asia/Beijing。
3. 环境准备 #
- 需要 Python 3.9+(内置
zoneinfo)。
- Windows 和 Docker 建议安装
tzdata。
3.1 安装与验证 #
py -m pip install tzdata
- 下载慢:
py -m pip install tzdata -i https://pypi.tuna.tsinghua.edu.cn/simple
from datetime import datetime
from zoneinfo import ZoneInfo
now = datetime.now(ZoneInfo("Asia/Shanghai"))
print("上海当前时间:", now)
print("验证通过")
4. 常用操作 #
- 日常开发只需掌握四个动作:获取当前时间、UTC 转本地、格式化、时间加减。
- 推荐用
datetime.now(timezone.utc) 获取 UTC,不要用已废弃的 datetime.utcnow()。
4.1 获取当前时间与 UTC 转本地 #
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
LOCAL_TZ = ZoneInfo("Asia/Shanghai")
utc_now = datetime.now(timezone.utc)
print("UTC:", utc_now)
print("上海:", datetime.now(LOCAL_TZ))
local = utc_now.astimezone(LOCAL_TZ)
print("转换:", local.strftime("%Y-%m-%d %H:%M:%S"))
4.2 数据库 naive 时间转本地 #
- 很多项目约定:数据库存 UTC 的 naive datetime(无时区后缀)。
- 读出后先
.replace(tzinfo=timezone.utc),再 .astimezone(LOCAL_TZ)。
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
LOCAL_TZ = ZoneInfo("Asia/Shanghai")
db_time = datetime(2026, 5, 30, 4, 30, 0)
utc_aware = db_time.replace(tzinfo=timezone.utc)
local_str = utc_aware.astimezone(LOCAL_TZ).strftime("%Y-%m-%d %H:%M")
print("页面显示:", local_str)
5. 项目实战:Flask 本地时间 #
- Web 项目里把 §4.2 的逻辑封装成模板过滤器即可。
- 模板中写
{{ row.created_at | localtime }} 显示北京时间。
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
LOCAL_TZ = ZoneInfo("Asia/Shanghai")
def format_localtime(dt, fmt="%Y-%m-%d %H:%M"):
"""将 UTC naive 时间格式化为上海本地时间字符串。"""
if not dt:
return ""
utc_dt = dt.replace(tzinfo=timezone.utc)
return utc_dt.astimezone(LOCAL_TZ).strftime(fmt)
print(format_localtime(datetime(2026, 5, 30, 4, 30, 0)))
6. 常见问题 #
6.1 ZoneInfoNotFoundError #
- 原因:未安装
tzdata,且系统无 IANA 数据。
- 解决:
py -m pip install tzdata,重启 Python 进程。
6.2 naive 与 aware 混用 #
- 对 naive datetime 直接
.astimezone() 会被当作本地时区,结果错误。
- 统一约定:数据库 UTC naive → 读出后
.replace(tzinfo=timezone.utc) → 再转换。
6.3 其他注意点 #
- 新项目用
zoneinfo,不必再引入 pytz(除非维护老代码)。
- 获取 UTC 用
datetime.now(timezone.utc),不用 datetime.utcnow()。
- 解析 ISO 字符串用
datetime.fromisoformat("2026-05-30T12:00:00+08:00")。
7. API 速查 #
- 下面汇总配合
tzdata 的日常写法(Python 3.9+ / tzdata 2025.x)。
| 操作 |
写法 |
| 安装 |
py -m pip install tzdata |
| 创建时区 |
ZoneInfo("Asia/Shanghai") |
| UTC 现在 |
datetime.now(timezone.utc) |
| 本地现在 |
datetime.now(ZoneInfo("Asia/Shanghai")) |
| naive → UTC aware |
dt.replace(tzinfo=timezone.utc) |
| 转本地 |
dt.astimezone(ZoneInfo("Asia/Shanghai")) |
| 格式化 |
dt.strftime("%Y-%m-%d %H:%M:%S") |
| 时间加减 |
dt + timedelta(hours=1) |
- 核心流程:存 UTC → 读出标记 UTC → astimezone(Asia/Shanghai) → strftime。
- Windows / Docker 务必安装
tzdata。