datetime 模块
上一章的 time 模块擅长处理时间戳、暂停和运行耗时。实际项目还经常需要表示某一天、安排任务时间、计算截止日期:如何让日期、时刻、时间差和时区信息保持清晰,并能继续参与计算?这些工作更适合使用 datetime 模块。
datetime 模块提供四个常用类:
| 类 | 表示的内容 | 示例 |
|---|---|---|
date | 日期 | 2026-09-12 |
time | 一天中的时间 | 09:30:00 |
datetime | 日期和时间 | 2026-09-12 09:30:00 |
timedelta | 一段时间 | 7 天、30 分钟 |
本章采用直接导入类的方式:
from datetime import date, datetime, time, timedelta这里的 time 是 datetime 模块中的时间类,不是上一章学习的 time 模块。
日期对象
date 对象只保存年、月、日:
from datetime import date
course_day = date(2026, 9, 12)
today = date.today()
print(course_day)
print(today)date.today() 返回当前系统的本地日期,因此每次运行结果都可能不同。手动创建日期时,年、月、日必须合法,否则会抛出 ValueError。
日期对象的常用属性和方法如下:
print(course_day.year)
print(course_day.month)
print(course_day.day)
print(course_day.weekday())
print(course_day.isoweekday())两种星期方法的编号规则不同:
| 方法 | 星期一 | 星期日 |
|---|---|---|
weekday() | 0 | 6 |
isoweekday() | 1 | 7 |
时间对象与日期时间对象
datetime.time 表示一天中的时、分、秒和微秒,不包含具体日期:
from datetime import time
class_time = time(9, 30, 15, 500000)
print(class_time)
print(class_time.hour)
print(class_time.minute)
print(class_time.second)
print(class_time.microsecond)datetime 对象同时保存日期和时间:
from datetime import datetime
started_at = datetime(2026, 9, 12, 9, 30, 15)
now = datetime.now()
print(started_at)
print(now)datetime.now() 返回当前系统的本地日期和时间。没有提供的时、分、秒默认是 0:
midnight = datetime(2026, 9, 12)
print(midnight)运行结果:
2026-09-12 00:00:00字段访问与时间组合
datetime 对象可以读取日期与时间字段,也可以分别取出两个部分:
print(started_at.year)
print(started_at.month)
print(started_at.day)
print(started_at.hour)
print(started_at.minute)
print(started_at.second)
print(started_at.date())
print(started_at.time())datetime.combine() 可以把 date 和 time 合成完整时间:
from datetime import date, datetime, time
course_day = date(2026, 9, 12)
class_time = time(9, 30)
started_at = datetime.combine(course_day, class_time)
print(started_at)运行结果:
2026-09-12 09:30:00不可变对象与 replace()
date、time 和 datetime 对象都不能直接修改。replace() 会根据原对象创建一个新对象:
from datetime import datetime
started_at = datetime(2026, 9, 12, 9, 30)
delayed_at = started_at.replace(hour=10, minute=15)
print(started_at)
print(delayed_at)原来的 started_at 不会改变。
日期时间与字符串
日期时间经常需要写入文件、显示在页面上,或者从用户输入中读取。
ISO 格式
标准日期字符串优先使用 isoformat() 和 fromisoformat(),不需要手写格式符:
from datetime import date, datetime
started_at = datetime(2026, 9, 12, 9, 30, 15)
print(started_at.isoformat())
print(date.fromisoformat("2026-09-12"))
print(datetime.fromisoformat("2026-09-12 09:30:15"))运行结果:
2026-09-12T09:30:15
2026-09-12
2026-09-12 09:30:15自定义格式
非 ISO 格式可以使用 strftime() 和 strptime():
from datetime import datetime
started_at = datetime(2026, 9, 12, 9, 30, 15, 123456)
text = started_at.strftime("%Y年%m月%d日 %H:%M:%S.%f")
parsed_at = datetime.strptime("2026/09/12 09:30", "%Y/%m/%d %H:%M")
print(text)
print(parsed_at)上一章已经介绍过常用格式符。本章新增的 %f 表示微秒,可以用于 datetime 的格式化和解析。
字符串与格式不一致,或者日期本身不合法时,strptime() 会抛出 ValueError。
时间差计算
timedelta 表示一段持续时间。创建时可以使用周、天、小时、分钟、秒、毫秒和微秒等参数:
from datetime import timedelta
duration = timedelta(days=1, hours=2, minutes=30)
print(duration)运行结果:
1 day, 2:30:00时间偏移
date 或 datetime 加减 timedelta,会得到新的日期时间对象:
from datetime import datetime, timedelta
started_at = datetime(2026, 9, 12, 9, 30)
deadline = started_at + timedelta(days=7, hours=2)
reminder = started_at - timedelta(minutes=30)
print(deadline)
print(reminder)timedelta 可以乘除数字,但不能与普通数字直接相加:
one_hour = timedelta(hours=1)
print(one_hour * 3)
print(one_hour / 2)如果执行 one_hour + 2,Python 无法判断数字 2 的单位,会抛出 TypeError。
时间间隔
两个 date 或两个 datetime 对象相减,会得到 timedelta:
from datetime import datetime
started_at = datetime(2026, 9, 12, 9, 30)
finished_at = datetime(2026, 9, 12, 11, 15)
duration = finished_at - started_at
print(duration)
print(duration.total_seconds())运行结果:
1:45:00
6300.0需要完整秒数时,应使用 total_seconds()。duration.seconds 只表示去掉整天后剩余的秒数,不一定等于整个时间差。
日期时间比较
同类且时区状态兼容的日期时间对象可以比较先后:
from datetime import datetime
start = datetime(2026, 9, 12, 9, 30)
deadline = datetime(2026, 9, 12, 12, 0)
current = datetime(2026, 9, 12, 10, 0)
print(current < deadline)
print(start <= current <= deadline)朴素时间与时区时间
datetime.now() 返回的对象通常不包含时区信息,这类对象称为朴素时间:
from datetime import datetime
local_now = datetime.now()
print(local_now.tzinfo)输出的 None 表示对象没有保存时区。仅看到 2026-09-12 09:30:00,无法判断它表示北京时间还是其他地区的时间。
带时区时间
Python 3.14 可以使用 UTC 获取带时区的 UTC 时间:
from datetime import UTC, datetime
utc_now = datetime.now(UTC)
local_now = datetime.now().astimezone()
print(utc_now)
print(local_now)datetime.now(UTC)返回带时区的 UTC 时间。datetime.now().astimezone()返回带系统本地时区的时间。
不要再使用 datetime.utcnow()。该方法从 Python 3.12 起已弃用,并且返回没有时区信息的对象。
时区规则比较复杂。本章只要求能够区分朴素时间和带时区时间;涉及跨地区业务时,应优先保存带时区的 UTC 时间,在显示时再转换成本地时间。
日期时间与时间戳
时间戳本身不保存时区,但转换成日期时间时,需要决定按本地时间还是 UTC 显示:
from datetime import UTC, datetime
timestamp = 0
local_time = datetime.fromtimestamp(timestamp)
utc_time = datetime.fromtimestamp(timestamp, UTC)
print(local_time)
print(utc_time)
print(utc_time.timestamp())其中:
- 省略时区参数时,
fromtimestamp()返回本地朴素时间,结果受系统时区影响。 - 传入
UTC时,返回带时区的 UTC 时间。 timestamp()可以把datetime对象转回时间戳。
不要使用从 Python 3.12 起已弃用的 datetime.utcfromtimestamp()。应使用 datetime.fromtimestamp(timestamp, UTC)。
对于朴素 datetime,timestamp() 会按照系统本地时间解释它;带时区对象的含义更明确。
综合案例:计算任务截止时间
需求: 为一项课程资料整理任务记录创建时间、计算截止时间,并根据检查时刻判断任务是否超时。
功能分析: 使用带时区的 datetime 表示时刻,使用 timedelta 计算截止时间,使用比较运算和格式化输出任务状态。
下面创建一个带时区的任务,计算截止时间,并判断检查时刻是否已经超时:
from datetime import UTC, datetime, timedelta
def create_task(title, created_at, duration_minutes):
return {
"title": title,
"created_at": created_at,
"deadline": created_at + timedelta(minutes=duration_minutes),
}
def show_task(task, checked_at):
time_format = "%Y-%m-%d %H:%M:%S %Z"
status = "已超时" if checked_at > task["deadline"] else "进行中"
print(f"任务名称:{task['title']}")
print(f"创建时间:{task['created_at'].strftime(time_format)}")
print(f"截止时间:{task['deadline'].strftime(time_format)}")
print(f"检查状态:{status}")
created_at = datetime(2026, 9, 12, 1, 30, tzinfo=UTC)
task = create_task("整理课程资料", created_at, 90)
checked_at = datetime(2026, 9, 12, 2, 45, tzinfo=UTC)
show_task(task, checked_at)运行结果:
任务名称:整理课程资料
创建时间:2026-09-12 01:30:00 UTC
截止时间:2026-09-12 03:00:00 UTC
检查状态:进行中这个案例串联了带时区的 datetime、timedelta、时间比较和格式化输出。
小结
date表示日期,time表示一天中的时间,datetime表示完整日期时间。timedelta表示时间差,可以参与日期时间加减,也可以乘除数字。- ISO 格式优先使用
isoformat()和fromisoformat()。 - 自定义格式使用
strftime()和strptime()。 - 朴素
datetime不包含时区信息,带时区对象的时刻含义更明确。 - Python 3.14 使用
datetime.now(UTC)获取 UTC 时间。 - 使用
fromtimestamp()时,应明确需要本地时间还是 UTC 时间。
练习
- 创建自己的生日
date对象,输出年月日和星期。 - 使用
datetime.combine()把明天的日期和08:30合并成完整时间。 - 把字符串
"2028/05/01 08:30"解析成datetime,再转换为 ISO 格式。 - 使用
timedelta(days=7)计算今天一周后的日期。 - 创建两个
datetime对象,计算它们相差多少秒。 - 修改综合案例的检查时间,让任务状态变成“已超时”。