Python时间日期处理函数怎么用?datetime模块核心方法详解
datetime 模块提供了完备的工业级解决方案。理解该模块的关键在于掌握三条主线:对象获取与构造、字符串双向转换(格式化与解析)以及时间差量(timedelta)的算术推移。本文将系统拆解 datetime 的核心类与高频方法,帮助你在实际项目中准确、高效地解决各类时间处理需求。
一、认识 datetime 模块的核心对象与分工
很多初学者容易将 datetime 模块当成一个单一的工具包,甚至误以为 datetime 只有一个类。实际上,标准库针对时间概念的不同维度进行了精巧的职责分离。datetime 模块内部主要包含以下五个核心类:
date类:仅表示纯日期信息,包含年(year)、月(month)、日(day)。适用于不需要关心具体时间点的业务场景,例如生日记录、公休假日清单、合同起止日等。time类:仅表示一天内的时间,包含时(hour)、分(minute)、秒(second)、微秒(microsecond)及可选的时区信息。它独立于具体的日期,适用于记录每日开盘时间、定时巡检时刻等。datetime类:最常用的核心枢纽,同时包含完整的日期与时间信息,相当于date与time的集合体。timedelta类:表示两个时间点之间的持续时间或差量。所有的日期加减、天数推移都必须借助它来完成。timezone / tzinfo类:负责抽象时区规则与 UTC 偏移量,用于将没有地理概念的“天真时间”(Naive)转化为具备明确时区属性的“感知时间”(Aware)。
二、获取与构建时间对象的核心方法
在编写业务逻辑时,首要任务就是实例化时间对象。无论是获取系统当前时刻、从时间戳还原,还是手工指定具体的年月日时分秒,datetime 都提供了简洁直接的方法。
1. 获取当前系统时间:now() 与 today()
获取当前本地时间最标准的方式是调用 datetime.now()。虽然也可以调用 datetime.today(),但 now() 的灵活性更高,因为它允许直接传入 tz 参数来获取特定时区的当前时间。
from datetime import datetime, timezone
# 获取系统当前本地时间(无时区信息的 Naive 对象)
local_now = datetime.now()
print("当前本地时间:", local_now)
# 获取带有时区感知的当前 UTC 时间(推荐实践)
utc_now = datetime.now(timezone.utc)
print("当前 UTC 时间:", utc_now)
示例输出:
当前本地时间: 2026-10-20 12:18:34.125080
当前 UTC 时间: 2026-10-20 04:18:34.125080+00:00
2. 静态构造指定时间点与属性访问
构造特定时刻只需向 datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0) 传入数值。构造完成后,可直接通过属性读取具体时间成分:
from datetime import datetime
# 构造 2026 年 10 月 1 日 08:30:00
target_dt = datetime(2026, 10, 1, 8, 30, 0)
print(f"年: {target_dt.year}, 月: {target_dt.month}, 日: {target_dt.day}")
print(f"时: {target_dt.hour}, 分: {target_dt.minute}, 秒: {target_dt.second}")
# 获取星期几(0 代表周一,6 代表周日)
print("星期索引:", target_dt.weekday())
3. 时间戳与 datetime 对象的互转
在与数据库交互、消息队列传输或前端接口对接时,经常使用自 1970 年 1 月 1 日(Epoch)以来的秒数(Unix 时间戳)。转换方法非常直观:
from datetime import datetime
# 1. 将 datetime 对象转换为浮点型秒级时间戳
current_ts = datetime.now().timestamp()
print("时间戳:", current_ts)
# 2. 从时间戳恢复为本地 datetime 对象
restored_dt = datetime.fromtimestamp(current_ts)
print("还原时间:", restored_dt)
三、字符串与时间的相互转换(格式化与解析)
数据在网络传输或日志落盘时通常表现为字符串文本,而在程序内部运算时则需要还原为 datetime 对象。这一进一出的桥梁正是 strptime 与 strftime。
1. strptime:将字符串解析为时间对象
方法名字中的 p 代表 parse(解析)。调用时必须显式提供与字符串完全一致的格式模板:
from datetime import datetime
date_str = "2026-10-20 15:30:00"
# 按照对应格式进行精准匹配
parsed_dt = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")
print("解析出的对象:", parsed_dt)
print("提取年份:", parsed_dt.year)
%f,Python 会直接抛出 ValueError: time data '...' does not match format '...'。
2. strftime:将时间对象格式化为字符串
方法名字中的 f 代表 format(格式化)。该方法由具体的 datetime 实例调用,将内存对象转化为所需风格的文本:
from datetime import datetime
now = datetime(2026, 10, 20, 15, 30, 0)
# 输出为常见的中国地区常用格式
formatted_str = now.strftime("%Y年%m月%d日 %H时%M分%S秒")
print("中文格式化:", formatted_str)
# 紧凑型时间戳格式(常用于文件命名)
compact_str = now.strftime("%Y%m%d_%H%M%S")
print("文件名格式:", compact_str)
3. 高频使用的格式控制符速查
| 控制符 | 含义 | 取值范围与说明 | 示例(以 2026-10-20 为例) |
|---|---|---|---|
%Y |
四位年份 | 完整四位数字 | 2026 |
%m |
月份 | 两位数字(前导补零 01-12) | 10 |
%d |
日(月中天) | 两位数字(前导补零 01-31) | 20 |
%H |
24小时制小时 | 00-23 | 15 |
%I |
12小时制小时 | 01-12 | 03 |
%M |
分钟 | 00-59 | 30 |
%S |
秒 | 00-59 | 00 |
%f |
微秒 | 000000-999999(六位微秒) | 125080 |
%z |
UTC 偏移量 | +HHMM 或 -HHMM | +0800 |
四、时间差量与计算——timedelta 的妙用
在业务逻辑中,常见的需求诸如“计算会员到期日(当前时间往后顺延 30 天)”、“获取昨天此时的统计快照”或“计算任务耗时”,这些都必须依赖 timedelta 类来完成。
1. 时间加减推移
timedelta 构造函数支持 days, seconds, microseconds, milliseconds, minutes, hours, weeks。直接将其加减到 datetime 对象上即可实现推移:
from datetime import datetime, timedelta
now = datetime(2026, 10, 20, 12, 0, 0)
# 计算 7 天后的时间
future_time = now + timedelta(days=7)
print("7 天后:", future_time)
# 计算 2 小时 30 分钟前的时间
past_time = now - timedelta(hours=2, minutes=30)
print("过去时间:", past_time)
2. 两个时间相减与 total_seconds() 的避坑要点
当两个 datetime 实例相减时,Python 会自动返回一个 timedelta 实例。很多开发者最容易在这里犯错:误用 delta.seconds 当作总相差秒数。
from datetime import datetime, timedelta
start = datetime(2026, 10, 20, 10, 0, 0)
end = datetime(2026, 10, 21, 10, 0, 15) # 相隔 1 天零 15 秒
delta = end - start
# 常见误区:以为 delta.seconds 是总秒数
print("delta.days:", delta.days) # 输出: 1
print("delta.seconds:", delta.seconds) # 输出: 15 (仅仅是不足一天的余数部分!)
# 正确做法:使用 total_seconds() 获取总秒数
total_sec = delta.total_seconds()
print("实际总秒数:", total_sec) # 输出: 86415.0
timedelta 在内部存储时,为了避免数值过大溢出,会自动归一化(Normalize)为仅由 days、seconds(0 到 86399 之间)和 microseconds(0 到 999999 之间)三个属性组成。因此,凡是涉及总耗时换算,永远应当使用 total_seconds()。
五、常见易混淆点与避坑指南
在 Python 时间处理的实践中,有几个经典陷阱经常导致线上数据异常或偶发报错。理解其背后的原理即可从根本上规避。
1. 为什么 Python 3.12 废弃了 utcnow()?
很多老旧教程依然推崇 datetime.utcnow()。但从 Python 3.12 起,官方正式将 datetime.utcnow() 标记为 Deprecated(已废弃)。原因在于:utcnow() 返回的是一个 Naive 对象(虽然时间数值是 UTC 的,但该对象本身并未附带任何时区标识)。
如果开发者误把这个对象当成本地时间写入数据库,或者直接拿它与带时区的感知对象进行运算,就会造成 8 小时的时区错位。现代推荐规范是始终显式传入 UTC 时区对象:
from datetime import datetime, timezone
# 推荐的标准写法
current_utc = datetime.now(timezone.utc)
print("标准 UTC 时间(带时区感知):", current_utc)
2. Naive 与 Aware 对象的混合比较异常
在 Python 中,未绑定时区的时间对象称为 Offset-naive,绑定了时区的时间对象称为 Offset-aware。两者直接进行比较或相减时,Python 会为了保证数据严肃性直接抛出异常:
from datetime import datetime, timezone
naive_dt = datetime.now()
aware_dt = datetime.now(timezone.utc)
try:
if naive_dt > aware_dt:
print("比较成功")
except TypeError as e:
print("错误提示:", e)
# 输出: TypeError: can't compare offset-naive and offset-aware datetimes
解决办法:在系统边界处统一时间形态。若需要统一为带时区对象,可使用 .astimezone() 进行显式转换;若只需要本地比较,则确保参与运算的双方都同为 Naive 或同为 Aware。
3. datetime 实例的不可变性(Immutable)
datetime、date 和 time 对象在 Python 内部均为不可变对象。你不能直接通过 dt.year = 2027 修改其属性,这样会触发 AttributeError。若需调整某个特定属性,必须使用 replace() 方法生成一个新的实例:
from datetime import datetime
original_dt = datetime(2026, 10, 20, 15, 30, 0)
# 将年份修改为 2027,其他属性保持不变
modified_dt = original_dt.replace(year=2027)
print("原始对象:", original_dt)
print("修改后新对象:", modified_dt)
六、全流程决策与核心方法速查
为了在实际编码过程中迅速找到最适用的方法,下方的决策导图梳理了从具体业务目标到标准库 API 的直接映射路径:
七、总结与生产实践建议
掌握 datetime 模块的关键不仅在于熟记各种格式控制符,更在于在系统设计初期确立清晰的时间处理边界。在工业级项目中,建议遵循以下三条黄金准则:
- 存储与传输统一使用 UTC:数据库时间字段、日志索引、内部微服务通信统一采用带时区标记的 UTC 时间或直接存储 Unix 秒级时间戳。绝对不要在数据持久层混存无时区信息的本地时间。
- 展示层单向转换:仅在最终面向用户渲染(Web 界面、移动端 App、导出报表)时,再利用
astimezone()转为用户当地时区,并通过strftime()输出友好文本。 - 时间跨度优先使用 total_seconds():在任何计算系统运行时长、缓存失效剩余时间、接口响应耗时的场景下,均通过
(end - start).total_seconds()进行计算,坚决避免直接使用delta.seconds导致跨天数据失真。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













