Python 3.9+
Python Datetime & Time 完整教程
涵盖 Python 标准库中 datetime 和 time 模块的全部核心功能,附带实际使用案例
1
datetime 模块概览
datetime 模块提供了处理日期和时间的类,比 time 模块更高级、更面向对象。
核心类一览 Python
import datetime
# datetime.date - 日期(年、月、日)
# datetime.time - 时间(时、分、秒、微秒)
# datetime.datetime - 日期 + 时间
# datetime.timedelta - 时间间隔
# datetime.timezone - 时区信息
# datetime.tzinfo - 时区抽象基类
2
date 类
构造与类方法
| 方法 | 说明 | 返回类型 |
|---|---|---|
date(year, month, day) | 构造日期对象 | date |
date.today() | 返回当前本地日期 | date |
date.fromtimestamp(ts) | 从时间戳构造日期 | date |
date.fromordinal(n) | 从序数日期构造(公历第N天) | date |
date.fromisoformat(s) | 从 ISO 8601 字符串构造 | date |
date.fromisocalendar(y,w,d) | 从 ISO 日历构造 | date |
实例属性与方法
| 属性/方法 | 说明 |
|---|---|
.year / .month / .day | 年 / 月 / 日 |
.weekday() | 星期几(0=周一,6=周日) |
.isoweekday() | 星期几(1=周一,7=周日) |
.isoformat() | ISO 8601 格式 'YYYY-MM-DD' |
.ctime() | 可读字符串 |
.strftime(format) | 按指定格式输出字符串 |
.timetuple() | 转为 time.struct_time |
.toordinal() | 返回公历序数 |
.isocalendar() | 返回 (year, week, weekday) 元组 |
.replace(year, month, day) | 返回替换后的新日期对象 |
实际案例
创建与获取属性 Python
from datetime import date
d1 = date(2026, 5, 27)
d2 = date.today()
d3 = date.fromtimestamp(1748304000)
d4 = date.fromisoformat('2026-05-27')
print(d1.year) # 2026
print(d1.month) # 5
print(d1.weekday()) # 2(星期三,0=周一)
# ISO 日历
year, week, day = d1.isocalendar()
print(f"{year}年第{week}周,星期{day}")
# 格式化输出
print(d1.strftime('%Y年%m月%d日')) # 2026年05月27日
日期比较与运算 Python
from datetime import date, timedelta
d1 = date(2026, 5, 27)
d2 = date(2026, 12, 31)
delta = d2 - d1
print(delta.days) # 218
d3 = d1 + timedelta(days=100) # 2026-09-04
d4 = d1 - timedelta(weeks=4) # 2026-04-29
# 计算年龄
birthday = date(1995, 8, 15)
today = date.today()
age = today.year - birthday.year
if (today.month, today.day) < (birthday.month, birthday.day):
age -= 1
print(f"年龄: {age}岁")
3
time 类
构造
创建时间对象 Python
from datetime import time
t1 = time(14, 30, 0) # 14:30:00
t2 = time(14, 30, 45, 123456) # 14:30:45.123456
t3 = time.fromisoformat('14:30:00+08:00') # 带时区
实例属性与方法
| 属性/方法 | 说明 |
|---|---|
.hour / .minute / .second / .microsecond | 时 / 分 / 秒 / 微秒 |
.tzinfo | 时区信息 |
.fold | 折叠标记(处理夏令时重叠) |
.isoformat() | ISO 8601 格式字符串 |
.strftime(format) | 按指定格式输出字符串 |
.replace(...) | 返回替换后的新时间对象 |
实际案例
时间格式化与操作 Python
from datetime import time
t = time(14, 30, 45, 123456)
print(t.isoformat()) # 14:30:45.123456
print(t.isoformat(timespec='minutes')) # 14:30
print(t.strftime('%I:%M %p')) # 02:30 PM
print(t.strftime('%H时%M分%S秒')) # 14时30分45秒
# 替换组件
t2 = t.replace(hour=9, minute=0) # 09:00:00.123456
# 比较
t3 = time(9, 0)
print(t < t3) # False(14:30 > 09:00)
4
datetime 类
构造与类方法
| 方法 | 说明 |
|---|---|
datetime(year, month, day, ...) | 构造 |
datetime.now(tz=None) | 当前本地日期时间 |
datetime.now(timezone.utc) | 当前 UTC 日期时间(推荐) |
datetime.fromtimestamp(ts, tz) | 从时间戳构造 |
datetime.fromisoformat(s) | 从 ISO 8601 字符串构造 |
datetime.combine(date, time) | 合并 date 和 time 对象 |
datetime.strptime(s, fmt) | 从字符串解析 |
实例属性与方法
| 属性/方法 | 说明 |
|---|---|
.date() | 提取日期部分 |
.time() | 提取时间部分 |
.timestamp() | 转为 Unix 时间戳 |
.isoformat() | ISO 8601 字符串 |
.strftime(format) | 格式化输出 |
.astimezone(tz) | 转换时区 |
.replace(...) | 替换组件,返回新对象 |
实际案例
创建、解析与时区转换 Python
from datetime import datetime, timezone, timedelta
dt1 = datetime(2026, 5, 27, 14, 30, 0)
dt2 = datetime.now()
dt3 = datetime.now(timezone.utc) # 推荐的 UTC 时间获取方式
# 提取日期和时间部分
d = dt1.date() # date 对象
t = dt1.time() # time 对象
# 从字符串解析
dt4 = datetime.strptime('2026-05-27 14:30:00', '%Y-%m-%d %H:%M:%S')
dt5 = datetime.fromisoformat('2026-05-27T14:30:00+08:00')
# 合并 date 和 time
dt6 = datetime.combine(date(2026, 5, 27), time(14, 30))
# 时区转换
cst = timezone(timedelta(hours=8))
dt_cst = datetime(2026, 5, 27, 14, 30, tzinfo=cst)
dt_utc = dt_cst.astimezone(timezone.utc) # UTC+08:00 → UTC
datetime 运算 Python
from datetime import datetime, timedelta
dt1 = datetime(2026, 5, 27, 10, 0, 0)
dt2 = datetime(2026, 6, 15, 18, 30, 0)
delta = dt2 - dt1
print(delta.days) # 19
print(delta.total_seconds()) # 1672200.0
# 加减运算
dt3 = dt1 + timedelta(days=7, hours=3) # 2026-06-03 13:00:00
dt4 = dt1 - timedelta(weeks=2) # 2026-05-13 10:00:00
# 排序
dates = [datetime(2026,12,25), datetime(2026,1,1), datetime(2026,7,4)]
sorted_dates = sorted(dates) # 按时间排序
5
timedelta 类
构造与属性
| 属性 | 说明 |
|---|---|
.days | 天数(可以为负数) |
.seconds | 秒数(0-86399) |
.microseconds | 微秒数(0-999999) |
.total_seconds() | 总秒数(浮点数) |
实际案例
运算与工作日计算 Python
from datetime import timedelta, datetime, date
# 基本运算
td1 = timedelta(days=5, hours=3)
td2 = timedelta(days=2, hours=8)
print(td1 + td2) # 7 days, 11:00:00
print(td1 * 3) # 15 days, 9:00:00
# 总秒数
td = timedelta(hours=2, minutes=30, seconds=45)
print(td.total_seconds()) # 9045.0
# 计算工作日
def count_workdays(start_date, end_date):
workdays = 0
current = start_date
while current <= end_date:
if current.weekday() < 5:
workdays += 1
current += timedelta(days=1)
return workdays
start = date(2026, 5, 1)
end = date(2026, 5, 31)
print(f"2026年5月工作日: {count_workdays(start, end)} 天")
6
timezone 类
创建与使用时区 Python
from datetime import datetime, timezone, timedelta
# 创建时区对象
utc = timezone.utc # UTC 时区
cst = timezone(timedelta(hours=8)) # UTC+8(中国标准时间)
est = timezone(timedelta(hours=-5)) # UTC-5(美国东部)
# 带时区的 datetime 操作
dt_cst = datetime(2026, 5, 27, 14, 30, tzinfo=cst)
dt_utc = dt_cst.astimezone(timezone.utc) # 06:30:00+00:00
dt_est = dt_cst.astimezone(est) # 01:30:00-05:00
# 格式化时区信息
print(dt_cst.strftime('%Y-%m-%d %H:%M:%S %Z')) # UTC+08:00
7
tzinfo 抽象基类 & zoneinfo
自定义时区
继承 tzinfo Python
from datetime import tzinfo, timedelta, datetime
class ChinaTimeZone(tzinfo):
def utcoffset(self, dt):
return timedelta(hours=8)
def tzname(self, dt):
return "CST"
def dst(self, dt):
return timedelta(0)
china_tz = ChinaTimeZone()
dt = datetime(2026, 5, 27, 14, 30, tzinfo=china_tz)
print(dt.strftime('%Z')) # CST
zoneinfo(Python 3.9+,推荐)
推荐:使用 zoneinfo 替代手动创建 timezone,支持 IANA 时区数据库,自动处理夏令时。
IANA 时区数据库 Python
from datetime import datetime
from zoneinfo import ZoneInfo
beijing = ZoneInfo('Asia/Shanghai')
new_york = ZoneInfo('America/New_York')
dt_beijing = datetime(2026, 5, 27, 14, 30, tzinfo=beijing)
dt_ny = dt_beijing.astimezone(new_york)
print(f"北京时间: {dt_beijing}")
print(f"纽约时间: {dt_ny}")
8
格式化与解析
strftime 格式化指令
| 指令 | 说明 | 示例 |
|---|---|---|
%Y | 四位年份 | 2026 |
%y | 两位年份 | 26 |
%m | 月份(01-12) | 05 |
%d | 日(01-31) | 27 |
%H | 24小时制小时(00-23) | 14 |
%I | 12小时制小时(01-12) | 02 |
%M | 分钟(00-59) | 30 |
%S | 秒(00-59) | 45 |
%f | 微秒 | 123456 |
%A | 星期全称 | Wednesday |
%B | 月份全称 | May |
%p | AM/PM | PM |
%j | 年内第几天 | 147 |
%Z | 时区名称 | CST |
%z | UTC 偏移 | +0800 |
综合示例
strftime 格式化 Python
from datetime import datetime
dt = datetime(2026, 5, 27, 14, 30, 45, 123456)
print(dt.strftime('%Y-%m-%d')) # 2026-05-27
print(dt.strftime('%Y/%m/%d %H:%M:%S')) # 2026/05/27 14:30:45
print(dt.strftime('%Y年%m月%d日 %H时%M分%S秒')) # 2026年05月27日 14时30分45秒
print(dt.strftime('%A, %B %d, %Y')) # Wednesday, May 27, 2026
print(dt.strftime('%I:%M %p')) # 02:30 PM
strptime 解析字符串 Python
from datetime import datetime
dt1 = datetime.strptime('2026-05-27', '%Y-%m-%d')
dt2 = datetime.strptime('May 27, 2026 2:30 PM', '%B %d, %Y %I:%M %p')
dt3 = datetime.strptime('20260527143045', '%Y%m%d%H%M%S')
dt4 = datetime.strptime('2026-05-27T14:30:00+08:00', '%Y-%m-%dT%H:%M:%S%z')
# 处理解析失败
try:
datetime.strptime('not a date', '%Y-%m-%d')
except ValueError as e:
print(f"解析失败: {e}")
9
常见实战场景
计算年龄
精确计算年龄 Python
from datetime import date
def calculate_age(birthdate):
today = date.today()
age = today.year - birthdate.year
if (today.month, today.day) < (birthdate.month, birthdate.day):
age -= 1
return age
birthday = date(1995, 8, 15)
print(f"年龄: {calculate_age(birthday)} 岁")
获取月份范围
本月第一天与最后一天 Python
from datetime import date
import calendar
def get_month_range(year, month):
first_day = date(year, month, 1)
last_day_num = calendar.monthrange(year, month)[1]
last_day = date(year, month, last_day_num)
return first_day, last_day
first, last = get_month_range(2026, 5)
print(f"2026年5月: {first} ~ {last}")
相对时间描述
"刚刚"、"3 分钟前" 等 Python
from datetime import datetime, timedelta
def relative_time(dt):
now = datetime.now()
diff = now - dt
if diff.days == 0:
if diff.seconds < 60: return "刚刚"
elif diff.seconds < 3600: return f"{diff.seconds // 60} 分钟前"
else: return f"{diff.seconds // 3600} 小时前"
elif diff.days == 1: return "昨天"
elif diff.days < 7: return f"{diff.days} 天前"
elif diff.days < 30: return f"{diff.days // 7} 周前"
elif diff.days < 365: return f"{diff.days // 30} 个月前"
else: return f"{diff.days // 365} 年前"
倒计时
到目标时间的倒计时 Python
from datetime import datetime, timedelta
def countdown(target_dt):
now = datetime.now()
if target_dt <= now: return "目标时间已过"
diff = target_dt - now
days = diff.days
hours, remainder = divmod(diff.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return f"还剩 {days}天 {hours}小时 {minutes}分 {seconds}秒"
new_year = datetime(2027, 1, 1, 0, 0, 0)
print(f"距离2027年: {countdown(new_year)}")
时区感知的日程安排
会议时间转换为各参会者的本地时间 Python
from datetime import datetime
from zoneinfo import ZoneInfo
def schedule_meeting(meeting_time_str, attendees_timezone):
beijing = ZoneInfo('Asia/Shanghai')
meeting_time = datetime.fromisoformat(meeting_time_str).replace(tzinfo=beijing)
print(f"会议时间 (北京时间): {meeting_time.strftime('%Y-%m-%d %H:%M')}")
for tz_name in attendees_timezone:
tz = ZoneInfo(tz_name)
local_time = meeting_time.astimezone(tz)
print(f"{tz_name}: {local_time.strftime('%Y-%m-%d %H:%M %Z')}")
schedule_meeting('2026-05-27T10:00:00', [
'Asia/Shanghai', 'America/New_York', 'Europe/London', 'Asia/Tokyo'
])
10
time 模块概览
time 模块提供了各种时间相关的函数,更底层,侧重于系统时间、性能测量和延迟。
11
时间戳与 struct_time
时间戳(Timestamp)
Unix 时间戳是从 1970-01-01 00:00:00 UTC 到现在的秒数(浮点数)。
获取时间戳 Python
import time
ts = time.time()
print(f"当前时间戳: {ts}")
print(f"整数秒: {int(ts)}")
print(f"毫秒: {int(ts * 1000)}")
# 纳秒精度(Python 3.7+)
ts_ns = time.time_ns()
print(f"纳秒时间戳: {ts_ns}")
struct_time
| 索引 | 属性 | 说明 | 范围 |
|---|---|---|---|
| 0 | tm_year | 年 | 如 2026 |
| 1 | tm_mon | 月 | 1-12 |
| 2 | tm_mday | 日 | 1-31 |
| 3 | tm_hour | 时 | 0-23 |
| 4 | tm_min | 分 | 0-59 |
| 5 | tm_sec | 秒 | 0-60 |
| 6 | tm_wday | 星期几 | 0-6 |
| 7 | tm_yday | 年内第几天 | 1-366 |
| 8 | tm_isdst | 夏令时标志 | 0/1/-1 |
12
核心函数
常用函数速览 Python
import time
# 获取当前时间戳
ts = time.time()
# 转为本地 struct_time
st = time.localtime()
print(f"年: {st.tm_year}, 月: {st.tm_mon}, 日: {st.tm_mday}")
# 转为 UTC struct_time
st_utc = time.gmtime()
# struct_time 转时间戳
ts = time.mktime(st)
# 格式化
formatted = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
# 解析字符串
st = time.strptime('2026-05-27', '%Y-%m-%d')
# 可读字符串
print(time.ctime()) # Wed May 27 14:30:45 2026
# 暂停执行
time.sleep(2.5) # 暂停 2.5 秒
13
性能计时函数
计时函数对比
| 函数 | 用途 | 包含 sleep | 受系统时间调整影响 |
|---|---|---|---|
time.time() | 墙钟时间 | 是 | 是 |
time.perf_counter() | 高精度计时 | 是 | 否 |
time.monotonic() | 单调计时 | 是 | 否 |
time.process_time() | CPU 时间 | 否 | 否 |
time.thread_time() | 线程 CPU 时间 | 否 | 否 |
实战:性能测量
perf_counter 与上下文管理器 Python
import time
from contextlib import contextmanager
@contextmanager
def timer(label=""):
"""计时上下文管理器"""
start = time.perf_counter()
yield
elapsed = time.perf_counter() - start
print(f"{label} 耗时: {elapsed:.6f} 秒")
# 使用
with timer("列表推导式"):
result = [i ** 2 for i in range(1_000_000)]
with timer("for 循环"):
result = []
for i in range(1_000_000):
result.append(i ** 2)
实战:令牌桶限流器
TokenBucket Python
import time
class TokenBucket:
def __init__(self, rate, capacity):
self.rate = rate
self.capacity = capacity
self.tokens = capacity
self.last_time = time.monotonic()
def consume(self, tokens=1):
now = time.monotonic()
elapsed = now - self.last_time
self.last_time = now
self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
if self.tokens >= tokens:
self.tokens -= tokens
return True
return False
def wait_and_consume(self, tokens=1):
while not self.consume(tokens):
time.sleep(0.01)
bucket = TokenBucket(rate=5, capacity=10)
for i in range(20):
bucket.wait_and_consume()
14
time 常见实战场景
运行计时器 Python
import time
class Timer:
def __init__(self):
self._start = None
self._laps = []
def start(self):
self._start = time.perf_counter()
self._laps = [self._start]
return self
def lap(self, label=""):
now = time.perf_counter()
lap_time = now - self._laps[-1]
total_time = now - self._start
self._laps.append(now)
return lap_time, total_time
def stop(self):
return time.perf_counter() - self._start
带超时的重试机制 Python
import time
def retry_with_timeout(func, timeout=10, interval=1):
start = time.monotonic()
retries = 0
while True:
try:
if func():
return True, retries
except Exception as e:
print(f"尝试 {retries + 1} 失败: {e}")
retries += 1
elapsed = time.monotonic() - start
if elapsed >= timeout:
return False, retries
time.sleep(interval)
日志时间戳 Python
import time
from datetime import datetime
class Logger:
def __init__(self, name="App"):
self.name = name
self._start_time = time.time()
def _get_timestamp(self):
return datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
def _get_elapsed(self):
return f"[{time.time() - self._start_time:.3f}s]"
def info(self, msg):
print(f"{self._get_timestamp()} {self._get_elapsed()} [{self.name}] INFO: {msg}")
↔
datetime 与 time 对比
📅 datetime 模块
- 面向对象,高层抽象
- 核心类:date, time, datetime, timedelta
- 内置 timezone 和 zoneinfo 支持
- 原生支持 timedelta 日期运算
- 适合业务逻辑中的日期时间处理
⏱ time 模块
- 函数式,底层系统调用
- 核心类型:struct_time, 时间戳
- 时区需手动处理
- 日期运算需手动计算
- 适合系统级操作、性能测量
互相转换 Python
import time
from datetime import datetime, date
# time -> datetime
ts = time.time()
dt = datetime.fromtimestamp(ts)
# datetime -> time
dt = datetime.now()
ts = dt.timestamp()
# time.struct_time -> datetime
st = time.localtime()
dt = datetime(*st[:6])
# datetime -> time.struct_time
dt = datetime.now()
st = dt.timetuple()
✓
常见问题与最佳实践
推荐:始终使用带时区的 datetime(
datetime.now(timezone.utc)),避免无时区的 naive datetime。避免:
datetime.utcnow() 已弃用;timedelta 不支持月份参数;%m 是月份而非分钟!时区处理最佳实践 Python
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
# ✅ 推荐:始终使用带时区的 datetime
dt_aware = datetime.now(timezone.utc)
# ❌ 避免:无时区的 datetime
dt_naive = datetime.now() # 没有时区信息
# ✅ 存储时转为 UTC
def to_utc(dt):
if dt.tzinfo is None:
raise ValueError("无法转换无时区的 datetime")
return dt.astimezone(timezone.utc)
# ✅ 显示时转为本地时间
def to_local(dt, tz_name='Asia/Shanghai'):
return dt.astimezone(ZoneInfo(tz_name))
字符串解析建议 Python
from datetime import datetime
from dateutil import parser # pip install python-dateutil
# ✅ 推荐:使用 dateutil 进行灵活解析
dt = parser.parse('2026-05-27 14:30:00')
dt = parser.parse('May 27, 2026')
# ✅ 或使用标准库的 fromisoformat
dt = datetime.fromisoformat('2026-05-27T14:30:00+08:00')
# ❌ 避免:手动拼接字符串处理日期
常见陷阱 Python
# 陷阱1:datetime.utcnow() 已弃用
# ✅ datetime.now(datetime.timezone.utc)
# 陷阱2:timedelta 不支持月份
# ✅ 使用 dateutil.relativedelta(months=1)
# 陷阱3:%m 是月份(01-12),分钟是 %M
# ✅ datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# ❌ datetime.now().strftime('%Y-%m-%d %H:%m:%S') # %m 输出月份!
# 陷阱4:time.sleep() 不精确,需 perf_counter 监控
# 陷阱5:weekday() 0=周一,isoweekday() 1=周一
datetime 速查
from datetime import datetime, date, time, timedelta, timezone
# 获取当前时间
now = datetime.now()
now_utc = datetime.now(timezone.utc)
today = date.today()
# 创建
dt = datetime(2026, 5, 27, 14, 30)
d = date(2026, 5, 27)
t = time(14, 30, 0)
# 解析
dt = datetime.strptime('2026-05-27', '%Y-%m-%d')
dt = datetime.fromisoformat('2026-05-27T14:30:00')
# 格式化
s = dt.strftime('%Y-%m-%d %H:%M:%S')
# 运算
tomorrow = datetime.now() + timedelta(days=1)
diff = dt2 - dt1
time 速查
import time
# 获取时间
ts = time.time()
st = time.localtime()
st_utc = time.gmtime()
# 转换
ts = time.mktime(st)
s = time.strftime('%Y-%m-%d', st)
st = time.strptime('2026-05-27', '%Y-%m-%d')
s = time.ctime(ts)
# 性能计时
start = time.perf_counter()
# ... 代码 ...
elapsed = time.perf_counter() - start
# 延迟
time.sleep(1) # 暂停1秒