datetime.now() 返回无时区信息的 naive 对象,不可靠;应显式绑定时区如 ZoneInfo("Asia/Shanghai");年份解析须用 "%Y" 避免 "%y" 的世纪推断错误;timedelta 相加不处理夏令时,应使用时区感知对象或 dateutil;time.time() 精度与稳定性受限于平台,关键场景需选用 perf_counter()、time_ns() 或 UTC 时间戳。

datetime.now() 没有时区信息,不是“当前本地时间”的可靠表示
很多人调用 datetime.now() 就以为拿到了系统本地时间,其实它返回的是一个 naive datetime —— 没有 tzinfo,无法参与时区换算,也不能安全地存入数据库或跨系统传输。
真正需要本地时间时,应显式绑定时区:
- 用
zoneinfo.ZoneInfo(Python 3.9+):datetime.now(ZoneInfo("Asia/Shanghai")) - 旧版本可用
pytz:datetime.now(pytz.timezone("Asia/Shanghai")) - 避免用
time.localtime()或datetime.fromtimestamp(time.time()),它们也返回 naive 对象,且受系统时区设置隐式影响
strftime("%Y") 和 "%y" 在跨世纪年份处理上容易出错
strftime("%y") 只输出两位年份,比如 2024 → "24",但反过来用 strptime("24", "%y") 会默认解释为 2024 年——这是由 datetime 内部的“世纪推断规则”决定的:它把 00–68 映射到 2000–2068,69–99 映射到 1969–1999。这个行为不可配置,也不透明。
实际项目中只要涉及年份输入/解析,一律用 "%Y":
立即学习“Python免费学习笔记(深入)”;
- 用户表单、API 参数、日志文件名中含年份时,强制要求四位格式
- 读取旧数据若只有两位年份,先人工确认业务范围(比如“所有数据都在 2000–2030 之间”),再用
replace(year=...)手动修正 - 别依赖
datetime.strptime(..., "%y")自动补全,它会在 2070 年突然开始解析错
timedelta 相加不处理夏令时跳变
timedelta 是纯物理时长(如 3600 秒),它和 datetime 相加时,不会考虑 DST(夏令时)切换带来的“钟表时间”偏移。例如在欧洲/Paris 时区,2024-10-27 凌晨 2:00–3:00 这一小时会重复出现(时钟回拨),此时 dt + timedelta(hours=1) 可能落到模糊时间点,且结果取决于底层 C 库实现,不可靠。
正确做法是用时区感知对象配合 astimezone() 或专门的时区运算库:
- 用
zoneinfo.ZoneInfo创建带时区的datetime,再用.astimezone()转换目标时区 - 需做“加 N 天”类业务逻辑时,优先用
date类型(它不涉及时区与 DST) - 真要处理“下周一”“本月最后一天”等语义化日期,用
dateutil.rrule或dateutil.relativedelta,它们内置了 DST 意识
time.time() 返回的是 Unix 时间戳,但精度和时钟源因平台而异
time.time() 看似简单,但它返回的是浮点秒数,背后依赖系统时钟(CLOCK_REALTIME)。在某些容器环境、虚拟机或老旧嵌入式设备上,该时钟可能被挂起、跳跃或漂移严重;Windows 上默认只精确到 ~15ms,Linux 通常能到微秒级但需看内核配置。
关键判断点:
- 做性能计时?改用
time.perf_counter()(单调、高精度、不受系统时间调整影响) - 记录事件时间戳用于审计或排序?优先用
time.time_ns()(Python 3.7+,纳秒整数,更稳定) - 跨服务时间对齐?别信本地
time.time(),走 NTP 同步后的datetime.now(ZoneInfo("UTC"))或直接对接外部时间服务
最麻烦的不是语法写错,而是那些“看起来运行正常、但隔几年或换台机器就崩”的时间逻辑。时区、DST、纪元推断、时钟源——每个都藏在 datetime 表面之下,不动手验证根本看不出问题。










