Python datetime 模块实用入门与避坑指南
不管是做爬虫存抓取记录、Web 后端处理订单截止时间或用户生日,还是数据分析中的时间序列处理,Python 的 datetime 都是绕不开的标准库神器——无需 pip install,开箱即用。本教程将带你快速掌握它的核心功能,并顺带指出几个新手常踩的坑。
核心模块与类梳理
先指出一个新手最容易混淆的细节:模块名是 datetime,模块内部恰好又嵌套了一个同名类 datetime。为了让代码更清晰,后面的示例都将精准导入所需的类,避免混淆。
获取当前日期与时间
最基础的操作,就是获取当前时刻(本地环境,时区相关内容会在后面介绍)。
# 精准导入 datetime 类(而非整个模块)
from datetime import datetime
# 获取当前本地日期与时间(精确到微秒,不带时区信息)
now_local = datetime.now()
print(now_local) # 示例输出:2023-07-15 14:30:45.123456
print(type(now_local)) # <class 'datetime.datetime'>
自定义创建日期与时间
有时我们不需要取“现在”,而是需要指定一个特定的年、月、日,甚至精确到时、分、秒、微秒。
from datetime import datetime
# 必传参数:年、月、日
dt_basic = datetime(2023, 7, 15)
print(dt_basic) # 2023-07-15 00:00:00
# 可选参数(按顺序):时、分、秒、微秒
dt_full = datetime(2023, 7, 15, 14, 30, 59, 987654)
print(dt_full) # 2023-07-15 14:30:59.987654
与时间戳的双向转换
什么是时间戳?
时间戳是指从 1970年1月1日 00:00:00 UTC+0(格林威治时间) 开始计算的秒数,Python 中返回的是一个带微秒小数的浮点数。它最大的优势是时区无关——无论你在东京还是纽约,同一瞬间的时间戳完全相同,因此非常适合持久化存储。
datetime 转时间戳
from datetime import datetime
dt = datetime(2023, 7, 15, 14, 30)
ts = dt.timestamp()
print(ts) # 示例输出:1689409800.0
避坑提示:本地时区影响
如果创建 dt 时未显式设置时区,timestamp() 会自动使用运行代码的本地时区来换算成 UTC 秒数。这在跨环境运行(比如本地 Windows、服务器 Linux)时可能产生问题,务必留意。
时间戳转 datetime
有两个常用方法,区别在于返回的是“本地时区”还是“UTC+0 时区”的 datetime:
from datetime import datetime
ts = 1689409800.0
# 方法1:转换为本地时区的 datetime(不带时区标记)
dt_local = datetime.fromtimestamp(ts)
# 方法2:转换为 UTC+0 时区的 datetime(不带时区标记)
dt_utc = datetime.utcfromtimestamp(ts)
print(dt_local) # 本地时区的 2023-07-15 14:30:00
print(dt_utc) # 若本地是 UTC+8,这里将是 2023-07-15 06:30:00
与字符串的双向转换
我们与用户、文件、数据库交互时,最常见到的往往是人类可读的字符串,而不是 datetime 对象,此时就需要双向转换。
字符串转 datetime(解析)
使用 strptime() 方法进行解析,核心要点是严格匹配格式符,格式符必须与字符串完全吻合,否则会报错。
from datetime import datetime
# 解析 ISO 标准格式(最常用)
date_iso = "2023-07-15 14:30:00"
dt_iso = datetime.strptime(date_iso, '%Y-%m-%d %H:%M:%S')
print(dt_iso) # 2023-07-15 14:30:00
# 解析中文自然格式
date_cn = "2023年7月15日 14:30"
dt_cn = datetime.strptime(date_cn, '%Y年%m月%d日 %H:%M')
print(dt_cn) # 2023-07-15 14:30:00
常用格式符速查表
datetime 转字符串(格式化)
使用 strftime() 方法,同样利用上述格式符,可以灵活定制输出样式。
from datetime import datetime
now = datetime.now()
# 输出人性化的英文格式
fmt_en = now.strftime('%a, %b %d %Y %H:%M:%S')
print(fmt_en) # 示例:Sat, Jul 15 2023 14:30:45
# 输出简洁的日志格式
fmt_log = now.strftime('%Y%m%d_%H%M%S')
print(fmt_log) # 示例:20230715_143045
日期时间的加减运算
想计算“3天后的截止日期”或“2小时前的抓取时刻”?这时需要用到 timedelta 类。它表示两个 datetime 之间的时间差,支持天、小时、分钟、秒、微秒的加减。
from datetime import datetime, timedelta
now = datetime.now()
# 10小时之后
print(now + timedelta(hours=10))
# 1天3小时之前
print(now - timedelta(days=1, hours=3))
# 两个 datetime 相减,得到 timedelta 对象
dt1 = datetime(2023, 7, 15)
dt2 = datetime(2023, 7, 18)
delta = dt2 - dt1
print(delta.days) # 3
简单的时区处理
Python 3.2 及以上版本内置了 timezone 类,配合 timedelta 即可处理基础的时区需求。
显式设置 datetime 的时区
前文避坑提示曾提到,默认创建的 datetime 没有时区标记。我们可以使用 replace() 方法,或直接在 now() 中传入 tzinfo 参数来设置时区。
from datetime import datetime, timezone, timedelta
# 创建 UTC+8(北京/上海/香港)时区对象
tz_utc8 = timezone(timedelta(hours=8))
# 方法1:在 now() 中直接传入时区
now_utc8 = datetime.now(tz_utc8)
print(now_utc8) # 示例输出:2023-07-15 14:30:45.123456+08:00
# 方法2:使用 replace() 替换时区(⚠️ 注意:不会自动换算时间!)
dt_local = datetime.now()
dt_utc8_replace = dt_local.replace(tzinfo=tz_utc8)
时区之间的转换
使用 astimezone() 方法来实现时区转换,前提是原 datetime 对象已携带时区标记。
from datetime import datetime, timezone, timedelta
# 先获取带 UTC+0 标记的当前时间
now_utc0 = datetime.now(timezone.utc)
# 转换为东京时间(UTC+9)
tz_utc9 = timezone(timedelta(hours=9))
now_tokyo = now_utc0.astimezone(tz_utc9)
print(now_tokyo) # 比 now_utc0 多9小时
最佳实践总结
- 存储时优先使用时间戳或带时区的 UTC datetime:时区无关,跨环境安全稳定。
- 显示时再转换为用户时区:例如 Web 后端读取用户浏览器的时区偏好,转换好后再返回给前端展示。
- 使用 strptime 解析时格式符必须严格匹配:不放过任何一个空格或标点符号。
小练习:带时区字符串转时间戳
综合运用前面学到的知识,试着编写一个小函数,将带有时区信息的日期字符串转换为时间戳。这里使用了 Python 3.9+ 内置的 zoneinfo 模块,处理起来更加直观。
from datetime import datetime
from zoneinfo import ZoneInfo
def to_utc_ts(dt_str: str, tz_str: str) -> float:
"""
将时区感知的日期字符串转换为 UTC 时间戳。
:param dt_str: 日期时间字符串,格式 'YYYY-MM-DD HH:MM:SS'
:param tz_str: 时区名,如 'Asia/Shanghai', 'America/New_York'
:return: UTC 时间戳
"""
# 1. 解析日期时间字符串
naive_dt = datetime.strptime(dt_str, '%Y-%m-%d %H:%M:%S')
# 2. 附加时区信息
aware_dt = naive_dt.replace(tzinfo=ZoneInfo(tz_str))
# 3. 转换为时间戳(后台自动计算 UTC 秒数)
return aware_dt.timestamp()
# 测试一下
if __name__ == '__main__':
ts_shanghai = to_utc_ts('2023-07-15 14:30:00', 'Asia/Shanghai')
print(ts_shanghai) # 1689409800.0
ts_newyork = to_utc_ts('2023-07-15 02:30:00', 'America/New_York')
print(ts_newyork) # 与上面相同,因为代表同一瞬间
掌握以上这些核心功能,就可以轻松应对日常开发中 90% 的日期与时间处理需求了。