人人都会AI编程

14.4 时间格式转换、时区问题、最佳实践

更新时间:2026-07-12

在日常开发中,时间处理最容易出错的三个地方就是:格式转换时区处理各种边界情况。本节聚焦最实用的操作方法和避坑指南。

一、时间格式转换

时间数据在系统中频繁以字符串datetime 对象两种形态出现,转换是基本功。

字符串 → datetime 对象:strptime

from datetime import datetime

date_str = "2025-03-15 14:30:00"
dt = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")
print(dt)  # 2025-03-15 14:30:00
  • strptime 是“string parse time”的缩写,需要你提供与字符串精确匹配的格式指令。
  • 常用格式符号速记:
  • %Y 四位年份,%m 两位月份,%d 两位日期
  • %H 24小时制小时,%M 分钟,%S
  • %f 微秒(6位),%z 时区偏移(如+0800),%Z 时区名称(如 CST)

datetime 对象 → 字符串:strftime

dt = datetime(2025, 3, 15, 14, 30)
formatted = dt.strftime("%Y年%m月%d日 %H:%M")
print(formatted)  # 2025年03月15日 14:30
  • strftime 是“string format time”,可以按需要任意组合格式。
  • 输出中文日期等展示性内容时特别方便。

时间戳与 datetime 互转

from datetime import datetime

# 时间戳 → datetime
ts = 1710484200
dt = datetime.fromtimestamp(ts)   # 本地时区
dt_utc = datetime.utcfromtimestamp(ts)  # UTC(已弃用,建议用timezone处理)

# datetime → 时间戳
dt = datetime(2025, 3, 15, 14, 30)
ts = dt.timestamp()  # 返回浮点数秒
  • 高版本 Python 中 datetime.utcfromtimestamp 已标记弃用,推荐显式使用时区:datetime.fromtimestamp(ts, tz=timezone.utc)

二、时区问题

时区是时间处理的头号坑点,核心矛盾在于:datetime 对象可以“无时区意识”(naive)或“有时区意识”(aware),混用会导致错误的结果。

创建 aware datetime 对象

from datetime import datetime, timezone, timedelta

# 使用 UTC 时区
dt_utc = datetime(2025, 3, 15, 14, 30, tzinfo=timezone.utc)

# 使用固定偏移(如 UTC+8)
cst = timezone(timedelta(hours=8))
dt_cst = datetime(2025, 3, 15, 14, 30, tzinfo=cst)

# 使用第三方库 pytz(更完整的时区数据库)
import pytz
shanghai_tz = pytz.timezone('Asia/Shanghai')
dt_sh = shanghai_tz.localize(datetime(2025, 3, 15, 14, 30))
  • 建议:在业务系统中,所有时间内部存储统一使用 UTC,只在显示时转换为用户本地时区。这样可以避免跨时区计算的无尽混乱。

时区转换

# 将 aware datetime 转为其他时区
dt_utc = datetime(2025, 3, 15, 6, 30, tzinfo=timezone.utc)
dt_sh = dt_utc.astimezone(cst)  # 转换为 UTC+8,得到 14:30

naive 和 aware 的陷阱

  • 对 naive datetime 调用 timestamp(),Python 会假定它是本地时间,这在服务器上可能违背预期。
  • 两个 naive datetime 可以相减,但无法正确跨越夏令时边界。因此务必尽量使用 aware datetime
  • Django、SQLAlchemy 等框架默认存储 naive datetime,需要配置 USE_TZ = True 等选项来开启时区支持。

三、处理时间的最佳实践

  1. 存储用 UTC,展示用本地

数据库、API 传输一律使用 UTC 时间,前端或报表层再根据用户所在时区转换。这样做数据库里的一条记录在任何地区看到的绝对时刻都是一致的。

  1. 统一使用 aware datetime

从一开始就养成所有 datetime 对象都带时区的习惯,能避免 90% 的时区 bug。项目代码中可以封装辅助函数,禁止创建 naive datetime。

  1. 善用 dateutil 解析自然语言时间

第三方库 python-dateutilparser.parse() 能自动识别大多数常见时间字符串格式,比 strptime 更灵活:

   from dateutil import parser
   dt = parser.parse("2025-03-15 14:30")
   dt = parser.parse("March 15, 2025 2:30 PM")
   
  1. 计算时间差用 timedelta,慎用数值运算
   from datetime import timedelta
   tomorrow = dt + timedelta(days=1)
   last_week = dt - timedelta(weeks=1)
   

避免直接加减 86400 秒等数值,因为夏令时切换日可能不是 24 小时。

  1. 获取当前时间明确时区
   # 推荐:获取当前 UTC 时间
   now_utc = datetime.now(timezone.utc)
   # 不推荐:无时区的“现在”
   now_naive = datetime.now()   # 你不知道它到底是什么时区
   
  1. 日志和系统监控时间也要统一

logging 模块的默认时间戳是本地时间且不带时区,建议配置为 UTC 并加上时区信息,排错时才能准确对应服务器事件。

  1. 使用 time 模块处理纯时间差或性能计时

当只需要计时、计时睡眠而不在乎具体日期时,time.time()time.perf_counter() 等更轻量且不必纠结时区。

  1. 测试时区相关代码

编写单元测试时,显式给函数传入指定的 aware datetime,或使用 monkeypatch 模拟不同时区,确保逻辑正确。

掌握这些转换方法和时区原则,就能让时间处理从“最容易出 bug”变为“最可控”的模块之一。