Python datetime模块常用日期函数与基本用法
系统讲解Python标准库datetime模块的核心对象与常用函数,涵盖当前时间获取、字符串解析与格式化、时间增减推算及常见避坑指南。
在日常 Python 开发中,日期与时间的处理几乎无处不在,无论是记录日志时间戳、计算会员到期日、统计任务耗时,还是解析接口返回的格式化时间字符串。Python 官方标准库自带的 datetime 模块提供了高精度且健壮的时间数据类型,无需额外安装第三方库即可完成绝大多数常见的时间操作。本文直接围绕 datetime 模块中最核心的几个类和高频函数展开,梳理其实用场景、转换方式、计算逻辑与典型避坑细节。
一、模块基础:核心类型与职责划分
datetime 模块并非单一的工具包,而是由几个分工明确的面向对象类型构建而成的。理解这几个类各自的职责,是写出清晰、安全代码的前提。初学者常常因为分不清 date、time 与 datetime 的区别而产生不必要的格式转换开销:
date类:仅表示理想化的公历日期,包含year、month、day三个属性,适用于生日、节假日、考勤签到等无须关注分钟与秒的场景。time类:仅表示一天的具体时间,包含hour、minute、second、microsecond,不附带年月日信息,常用于作息设置或定时任务时间点匹配。datetime类:将date与time结合在一起,包含完整的年、月、日、时、分、秒以及微秒信息,是实际项目中最通用的核心类。timedelta类:表示两个时间点之间的时间跨度(差值),支持进行加减法推算。timezone类:负责时区偏移量的描述,用于感知时间(Aware DateTime)与 UTC/本地时间转换。
二、获取与构建日期时间对象
处理日期时间的第一步通常是获得一个有效的对象。常见来源有两种:获取当前的运行系统时间,或者根据已知数值手动创建指定的时间点。
1. 获取当前系统时间
通过 datetime.now() 可以获取本地当前时刻。如果程序部署在服务器上或需要统一使用 UTC 标准时间,推荐使用传入时区参数的方式:
from datetime import datetime, timezone
# 1. 获取本地当前时间(包含年月日时分秒与微秒)
now = datetime.now()
print("本地当前时间:", now)
# 示例输出: 2026-11-01 16:05:12.384912
# 2. 提取常用时间属性
print(f"年: {now.year}, 月: {now.month}, 日: {now.day}")
print(f"时: {now.hour}, 分: {now.minute}, 秒: {now.second}")
# 3. 获取 UTC 标准时间 (推荐写法)
utc_now = datetime.now(timezone.utc)
print("UTC标准时间:", utc_now)
# 示例输出: 2026-11-01 08:05:12.384912+00:00
注意:在早期的 Python 教程中常常见到 datetime.utcnow(),但该函数返回的是不带时区信息的纯朴素时间,极易在与其它时区计算时引发歧义。自 Python 3.12 起,官方已正式将 utcnow() 标记为 Deprecated(弃用),建议一律显式传入 timezone.utc。
2. 手动构建指定时间与纯日期
直接调用类的构造方法传入对应整数,即可构造出固定的时间实例:
from datetime import date, time, datetime
# 构建纯日期(年, 月, 日)
d = date(2025, 10, 1)
print("国庆日期:", d)
# 构建指定时间(时, 分, 秒)
t = time(18, 30, 0)
print("下班时间:", t)
# 合并日期与时间
dt_combined = datetime.combine(d, t)
print("组合完整时间:", dt_combined)
# 示例输出: 2025-10-01 18:30:00
三、核心技能:字符串与日期对象的相互转换
在前后端交互、数据库持久化或日志读取过程中,时间通常以字符串(例如 "2025-06-15 12:00:00")的形式存在。在 Python 中,这两者的桥梁是两个名字极为相似的函数:strptime 和 strftime。
strptime(Parse):将字符串解析为datetime对象。记忆法:“p”代表 Parse(解析)。strftime(Format):将datetime对象格式化输出为字符串。记忆法:“f”代表 Format(格式化)。
1. 常用格式化符号一览
| 符号 | 含义 | 示例值 | 特别提醒 |
|---|---|---|---|
%Y |
四位完整年份 | 2025, 2026 | 相对的小写 %y 只输出后两位年份(如 25) |
%m |
两位月份(01-12) | 05, 11 | 切勿与分钟的大写 %M 混淆 |
%d |
月内日期(01-31) | 01, 28 | 补零格式,如需无前导零通常用特定平台修饰 |
%H |
24小时制小时(00-23) | 08, 22 | 12小时制请使用 %I 配合 %p |
%M |
分钟数(00-59) | 05, 59 | 与月份 %m 大小写区分 |
%S |
秒数(00-59) | 00, 36 | 微秒请使用 %f |
2. 实操示例:解析与格式化
from datetime import datetime
# 1. 字符串解析为 datetime (strptime)
time_str = "2025-03-15 14:28:45"
format_pattern = "%Y-%m-%d %H:%M:%S"
dt_parsed = datetime.strptime(time_str, format_pattern)
print("解析后的对象:", type(dt_parsed), dt_parsed)
# 示例输出: 2025-03-15 14:28:45
# 2. datetime 格式化为自定义字符串 (strftime)
custom_str = dt_parsed.strftime("%Y年%m月%d日 %H时%M分")
print("自定义中文格式:", custom_str)
# 示例输出: 2025年03月15日 14时28分
# 3. 快捷标准格式 (ISO-8601)
print("标准ISO格式:", dt_parsed.isoformat())
# 示例输出: 2025-03-15T14:28:45
四、时间运算与差值计算:timedelta 的应用
在实际开发中,经常需要计算“3天前的时间”、“下周五是什么日期”或者“两个事件间隔了多长时间”。在 Python 中,直接对 datetime 进行普通的加减数字会抛出 TypeError,所有的时间步长与跨度都必须通过 timedelta 表达。
1. 时间向前与向后推算
timedelta 接收天数(days)、秒数(seconds)、毫秒、微秒、分钟(minutes)、小时(hours)、周(weeks)等命名参数:
from datetime import datetime, timedelta
current_time = datetime(2025, 5, 20, 10, 0, 0)
# 1. 计算 3 天后的时间
future_date = current_time + timedelta(days=3)
print("3天后:", future_date)
# 示例输出: 2025-05-23 10:00:00
# 2. 计算 2 小时 30 分钟前的时间
past_time = current_time - timedelta(hours=2, minutes=30)
print("2.5小时前:", past_time)
# 示例输出: 2025-05-20 07:30:00
2. 两个时间点相减统计耗时
直接让两个 datetime 相减即可获取时间跨度。需要注意的是,timedelta 内部只存储 days、seconds 与 microseconds:
t1 = datetime(2025, 6, 1, 8, 0, 0)
t2 = datetime(2025, 6, 3, 11, 30, 20)
duration = t2 - t1
print("跨度对象:", duration)
# 示例输出: 2 days, 3:30:20
print("相隔天数 (duration.days):", duration.days) # 2
print("不足一天的余秒 (duration.seconds):", duration.seconds) # 12620
print("总间隔秒数 (total_seconds()):", duration.total_seconds()) # 185420.0
五、常见易错陷阱与防护措施
在日常使用 datetime 时,有些问题并不会立即报错,而是在边界数据(如跨月、跨年、跨越时区)时产生严重的业务逻辑异常。以下四个陷阱是团队排查代码时最常遇到的点:
1. 混淆 timedelta.seconds 与 total_seconds()
这是最经典的统计 Bug 之一。开发者若需要将两个时间间隔转为小时,常常误写成 duration.seconds / 3600。实际上,duration.seconds 仅仅表示“扣除完整整天后,剩余不足一天的时间对应的秒数”(取值范围永远在 0 到 86399 之间)。如果两时间相差 3 天零 1 小时,duration.seconds 仅仅会返回 3600(1小时),直接丢失整整 3 天。因此,统计总时间跨度时,必须统一使用 duration.total_seconds() 方法。
2. 朴素时间 (Naive) 与感知时间 (Aware) 混用
没有时区信息的 datetime 称为 Naive 时间(例如 datetime.now() 默认返回的对象);带有具体时区偏移的称为 Aware 时间(例如 datetime.now(timezone.utc))。如果试图对两者直接进行大小比对或相减运算,Python 将直接抛出致命错误:
# 错误示例:混用 Naive 与 Aware
t_naive = datetime.now()
t_aware = datetime.now(timezone.utc)
# 会触发 TypeError: can't subtract offset-naive and offset-aware datetimes
try:
delta = t_naive - t_aware
except TypeError as e:
print("报错捕获:", e)
在涉及分布式服务、外部 API 调用或跨国部署的业务场景中,最安全的做法是统一在系统边界处将输入时间全部标准化为带时区的 UTC 时间,入库或对外传输均使用 ISO-8601 标准字符串。
3. replace() 与 timedelta 运算的语义差异
replace() 方法用于替换时间对象的特定字段(如将年份改为 2026),它是“重置”而非“推移”。如果试图用 dt.replace(day=dt.day + 10) 来实现日期加 10 天,一旦当前日期超过 21 日,加 10 就会导致 day > 31,从而直接引发 ValueError: day is out of range for month。涉及相对时间偏移的运算,永远应当使用 timedelta。
六、全文总结
Python 的 datetime 模块设计紧凑而严谨。在实际编写代码时,可以牢记以下核心要诀:
- 选对类型:只需要年月日用
date,需要完整时点用datetime,时间长度计算一律用timedelta。 - 记牢口诀:
strptime带有p即 Parse(字符串解析为对象);strftime带有f即 Format(对象格式化为字符串)。 - 注意统计范围:计算时间跨度总秒数务必使用
total_seconds(),严禁只读取.seconds。 - 时区意识:编写跨系统或对外服务的代码时,尽量显式使用
timezone.utc,避免 Naive 时间在不同地区服务器运行产生混淆。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









