🐍 datetime 模块 — 时间处理不再头疼
🕐 预计用时:2-3 小时 | 🎯 目标:掌握 date/time/datetime/timedelta、格式化、时间戳、时区
📖 今日目录
- strftime / strptime — 格式化与解析
1. 为什么需要 datetime?
datetime 模块是 Python 处理日期和时间的标准库,几乎任何涉及时间的程序都离不开它。
常见场景:
# datetime 模块有 5 个核心类
from datetime import date, time, datetime, timedelta, timezone
# 它们的关系:
# date → 只有年月日 → 2024-01-15
# time → 只有时分秒 → 14:30:00
# datetime → 年月日 + 时分秒 → 2024-01-15 14:30:00
# timedelta → 时间差(几天几小时)→ 3 days, 2:00:00
# timezone → 时区信息 → UTC+8
2. date 类 — 只有日期
date 表示一个日期:年、月、日。
from datetime import date
# 创建日期
d1 = date(2024, 1, 15) # 指定年月日
d2 = date.today() # 今天的日期
d3 = date.fromisoformat("2024-01-15") # 从 ISO 格式解析
print(d1) # 2024-01-15
print(d2) # 2024-01-15(今天的日期)
print(d3) # 2024-01-15
# 获取年月日
print(d1.year) # 2024
print(d1.month) # 1
print(d1.day) # 15
# 获取星期几(0=周一,6=周日)
print(d1.weekday()) # 0(周一)
print(d1.isoweekday()) # 1(ISO 标准,1=周一)
# 星期几的名称
days = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
print(f"{d1} 是 {days[d1.weekday()]}") # 2024-01-15 是 周一
date 的运算
from datetime import date, timedelta
d1 = date(2024, 1, 15)
d2 = date(2024, 2, 28)
# 两个日期相减 → 返回 timedelta
diff = d2 - d1
print(diff) # 44 days, 0:00:00
print(diff.days) # 44
# 日期 + timedelta → 新日期
d3 = d1 + timedelta(days=30)
print(d3) # 2024-02-14
# 日期 - timedelta → 新日期
d4 = d2 - timedelta(weeks=2)
print(d4) # 2024-02-14
# 比较日期
print(d1 < d2) # True
print(d1 == d2) # False
3. time 类 — 只有时间
time 表示一个时间:时、分、秒、微秒。
from datetime import time
# 创建时间
t1 = time(14, 30, 0) # 14:30:00
t2 = time(8, 15, 30, 500) # 08:15:30.000500(含微秒)
print(t1) # 14:30:00
print(t2) # 08:15:30.000500
# 获取时分秒
print(t1.hour) # 14
print(t1.minute) # 30
print(t1.second) # 0
# 时间比较
print(t1 > t2) # True(14:30 比 08:15 晚)
💡 time 类用得比较少,大多数时候你会直接用 datetime 类,它同时包含日期和时间。
4. datetime 类 — 日期 + 时间
datetime 是最常用的类,同时包含年月日和时分秒。
from datetime import datetime
# 创建 datetime
dt1 = datetime(2024, 1, 15, 14, 30, 0) # 指定全部
dt2 = datetime.now() # 当前时间
dt3 = datetime.today() # 同 now()
dt4 = datetime.fromisoformat("2024-01-15T14:30:00") # ISO 格式
print(dt1) # 2024-01-15 14:30:00
print(dt2) # 2024-01-15 14:30:25.123456(含微秒)
# 获取各部分
print(dt1.year) # 2024
print(dt1.month) # 1
print(dt1.day) # 15
print(dt1.hour) # 14
print(dt1.minute) # 30
print(dt1.second) # 0
print(dt1.weekday()) # 0(周一)
只取日期或时间部分
from datetime import datetime
dt = datetime(2024, 1, 15, 14, 30, 0)
# datetime → date(只要日期)
d = dt.date()
print(d) # 2024-01-15
print(type(d)) # <class 'datetime.date'>
# datetime → time(只要时间)
t = dt.time()
print(t) # 14:30:00
print(type(t)) # <class 'datetime.time'>
datetime 的运算
from datetime import datetime, timedelta
dt1 = datetime(2024, 1, 15, 14, 30)
dt2 = datetime(2024, 3, 20, 10, 0)
# 相减 → timedelta
diff = dt2 - dt1
print(diff) # 65 days, 19:30:00
print(diff.days) # 65
print(diff.seconds) # 70200(19小时30分钟 = 70200秒)
# 总秒数
print(diff.total_seconds()) # 5687400.0
# 加减 timedelta
dt3 = dt1 + timedelta(days=7, hours=2)
print(dt3) # 2024-01-22 16:30:00
5. timedelta — 时间差计算
timedelta 表示一段时间间隔,用来做日期的加减运算。
from datetime import timedelta
# 创建 timedelta
td1 = timedelta(days=7) # 7 天
td2 = timedelta(hours=3, minutes=30) # 3 小时 30 分
td3 = timedelta(weeks=2, days=3) # 2 周 3 天 = 17 天
td4 = timedelta(days=1, seconds=3600) # 1 天 1 小时
print(td1) # 7 days, 0:00:00
print(td2) # 3:30:00
print(td3) # 17 days, 0:00:00
print(td4) # 1 day, 1:00:00
# timedelta 的属性
print(td3.days) # 17(天数)
print(td3.total_seconds()) # 1468800.0(总秒数)
实用计算
from datetime import datetime, timedelta
now = datetime.now()
# 3 天后
three_days_later = now + timedelta(days=3)
print(f"3天后: {three_days_later}")
# 2 小时前
two_hours_ago = now - timedelta(hours=2)
print(f"2小时前: {two_hours_ago}")
# 下周一
days_until_monday = (7 - now.weekday()) % 7
if days_until_monday == 0:
days_until_monday = 7
next_monday = now + timedelta(days=days_until_monday)
print(f"下周一: {next_monday.date()}")
# 30 天前是几号
thirty_days_ago = now - timedelta(days=30)
print(f"30天前: {thirty_days_ago.date()}")
# 100 天后是星期几
days = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
hundred_days_later = now + timedelta(days=100)
print(f"100天后: {hundred_days_later.date()} {days[hundred_days_later.weekday()]}")
💡 timedelta 万能公式:
• 未来 = 现在 + timedelta(参数)
• 过去 = 现在 - timedelta(参数)
• 间隔 = 日期A - 日期B → timedelta
6. strftime / strptime — 格式化与解析
strftime = string format time(格式化输出)
strptime = string parse time(解析字符串)
strftime — datetime → 字符串
from datetime import datetime
now = datetime.now()
# 常用格式化
print(now.strftime("%Y-%m-%d")) # 2024-01-15
print(now.strftime("%Y/%m/%d %H:%M:%S")) # 2024/01/15 14:30:25
print(now.strftime("%Y年%m月%d日")) # 2024年01月15日
print(now.strftime("%H:%M")) # 14:30
print(now.strftime("%Y-%m-%d %H:%M:%S")) # 2024-01-15 14:30:25
# 中文格式
weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
print(f"{now.strftime('%Y年%m月%d日')} {weekdays[now.weekday()]}")
# 2024年01月15日 周一
常用格式符速查
| | |
|---|
%Y | | |
%m | | |
%d | | |
%H | | |
%I | | |
%M | | |
%S | | |
%p | | |
%A | | |
%a | | |
%B | | |
%b | | |
%w | | |
%j | | |
%W | | |
strptime — 字符串 → datetime
from datetime import datetime
# 从字符串解析
dt1 = datetime.strptime("2024-01-15", "%Y-%m-%d")
print(dt1) # 2024-01-15 00:00:00
print(type(dt1)) # <class 'datetime.datetime'>
dt2 = datetime.strptime("2024/01/15 14:30:00", "%Y/%m/%d %H:%M:%S")
print(dt2) # 2024-01-15 14:30:00
dt3 = datetime.strptime("15-01-2024", "%d-%m-%Y")
print(dt3) # 2024-01-15 00:00:00
# 常见日期格式
formats = [
("2024-01-15", "%Y-%m-%d"),
("2024/01/15", "%Y/%m/%d"),
("Jan 15, 2024", "%b %d, %Y"),
("15-Jan-2024", "%d-%b-%Y"),
("20240115", "%Y%m%d"),
]
for text, fmt in formats:
dt = datetime.strptime(text, fmt)
print(f"'{text}' → {dt}")
⚠️ strptime 的格式必须严格匹配!
datetime.strptime("2024-1-15", "%Y-%m-%d") → ❌ ValueError
月份必须是两位:"2024-01-15" → ✅
7. 时间戳 — 时间的数字表示
时间戳(timestamp)是从 1970-01-01 00:00:00 UTC 到现在的总秒数,是计算机存储时间的标准方式。
from datetime import datetime
now = datetime.now()
# datetime → 时间戳
ts = now.timestamp()
print(ts) # 1705312225.123456(浮点数)
# 时间戳 → datetime
dt = datetime.fromtimestamp(ts)
print(dt) # 2024-01-15 14:30:25.123456
# 时间戳 → UTC datetime
dt_utc = datetime.utcfromtimestamp(ts)
print(dt_utc) # 2024-01-15 06:30:25.123456(UTC 时间)
为什么时间戳很重要?
from datetime import datetime
import time
# 1. 精确计算时间差
start = time.time()
# 模拟耗时操作
total = 0
for i in range(1000000):
total += i
elapsed = time.time() - start
print(f"循环耗时: {elapsed:.4f} 秒") # 循环耗时: 0.0523 秒
# 2. 文件修改时间
import os
mtime = os.path.getmtime("students.csv")
dt = datetime.fromtimestamp(mtime)
print(f"students.csv 最后修改: {dt}")
# 3. 当前时间戳(用于 API 请求等)
print(int(time.time())) # 1705312225
8. 时区处理
时区是时间处理中最容易出错的部分。Python 用 timezone 和 tzinfo 来处理。
from datetime import datetime, timezone, timedelta
now = datetime.now()
print(f"本地时间(无时区信息): {now}")
# 创建带时区的时间
utc = datetime.now(timezone.utc)
print(f"UTC 时间: {utc}")
# 手动创建时区
tz_shanghai = timezone(timedelta(hours=8)) # UTC+8
tz_tokyo = timezone(timedelta(hours=9)) # UTC+9
tz_new_york = timezone(timedelta(hours=-5)) # UTC-5
# 带时区的时间
dt_shanghai = datetime(2024, 1, 15, 14, 30, tzinfo=tz_shanghai)
print(f"上海: {dt_shanghai}")
# 上海: 2024-01-15 14:30:00+08:00
# 转换时区
dt_tokyo = dt_shanghai.astimezone(tz_tokyo)
print(f"东京: {dt_tokyo}")
# 东京: 2024-01-15 15:30:00+09:00(东京比上海快1小时)
dt_ny = dt_shanghai.astimezone(tz_new_york)
print(f"纽约: {dt_ny}")
# 纽约: 2024-01-15 01:30:00-05:00(纽约比上海慢13小时)
用 zoneinfo 处理标准时区(Python 3.9+)
from datetime import datetime
from zoneinfo import ZoneInfo
# 标准时区名称
beijing = ZoneInfo("Asia/Shanghai")
tokyo = ZoneInfo("Asia/Tokyo")
new_york = ZoneInfo("America/New_York")
london = ZoneInfo("Europe/London")
# 当前各时区时间
now = datetime.now(beijing)
print(f"北京: {now.strftime('%Y-%m-%d %H:%M')}")
now_tokyo = now.astimezone(tokyo)
print(f"东京: {now_tokyo.strftime('%Y-%m-%d %H:%M')}")
now_ny = now.astimezone(new_york)
print(f"纽约: {now_ny.strftime('%Y-%m-%d %H:%M')}")
now_lon = now.astimezone(london)
print(f"伦敦: {now_lon.strftime('%Y-%m-%d %H:%M')}")
💡 时区最佳实践:
1. 存储时间用 UTC(datetime.now(timezone.utc))
2. 显示时间再转本地时区
3. 用 ZoneInfo("Asia/Shanghai") 而不是 timedelta(hours=8)(会自动处理夏令时)
9. 实战:倒计时与年龄计算器
倒计时器
from datetime import datetime, timedelta
def countdown(target_date_str, target_name):
"""计算距离目标日期还有多久"""
target = datetime.strptime(target_date_str, "%Y-%m-%d")
now = datetime.now()
if target < now:
print(f"❌ {target_name} 已经过去了!")
return
diff = target - now
days = diff.days
hours, remainder = divmod(diff.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
print(f"⏳ 距离 {target_name} 还有:")
print(f" {days} 天 {hours} 小时 {minutes} 分钟 {seconds} 秒")
# 使用
countdown("2025-01-01", "元旦")
# ⏳ 距离 元旦 还有:
# 351 天 9 小时 29 分钟 35 秒
countdown("2025-02-01", "春节")
countdown("2025-10-01", "国庆节")
年龄计算器
from datetime import date
def calculate_age(birth_date_str):
"""计算精确年龄"""
birth = date.fromisoformat(birth_date_str)
today = date.today()
# 基本年龄
age = today.year - birth.year
# 如果今年生日还没到,减 1
if (today.month, today.day) < (birth.month, birth.day):
age -= 1
# 距离下一个生日
next_birthday = date(today.year, birth.month, birth.day)
if next_birthday < today:
next_birthday = date(today.year + 1, birth.month, birth.day)
days_until = (next_birthday - today).days
print(f"🎂 出生日期: {birth}")
print(f"📅 今天日期: {today}")
print(f"👤 年龄: {age} 岁")
print(f"⏳ 距离下一个生日: {days_until} 天")
return age
# 使用
calculate_age("1995-06-15")
# 🎂 出生日期: 1995-06-15
# 📅 今天日期: 2024-01-15
# 👤 年龄: 28 岁
# ⏳ 距离下一个生日: 152 天
时间格式化工具
from datetime import datetime
def format_time_friendly(dt):
"""把时间转成友好的相对描述"""
now = datetime.now()
diff = now - dt
seconds = diff.total_seconds()
if seconds < 60:
return "刚刚"
elif seconds < 3600:
minutes = int(seconds // 60)
return f"{minutes} 分钟前"
elif seconds < 86400:
hours = int(seconds // 3600)
return f"{hours} 小时前"
elif seconds < 604800:
days = int(seconds // 86400)
return f"{days} 天前"
else:
return dt.strftime("%Y-%m-%d")
# 使用
from datetime import timedelta
now = datetime.now()
print(format_time_friendly(now - timedelta(seconds=30))) # 刚刚
print(format_time_friendly(now - timedelta(minutes=5))) # 5 分钟前
print(format_time_friendly(now - timedelta(hours=3))) # 3 小时前
print(format_time_friendly(now - timedelta(days=2))) # 2 天前
print(format_time_friendly(now - timedelta(days=30))) # 2024-12-16
10. 今日小结
| | |
|---|
date | | .today() |
time | | .hour |
datetime | | .now() |
timedelta | | .days |
timezone | | .astimezone() |
核心要点
- ✅
datetime.now() 获取当前日期+时间 - ✅ 日期相减 →
timedelta,日期 + timedelta → 新日期 - ✅
strftime() 格式化输出,strptime() 解析字符串 - ✅ 时间戳 = 从 1970-01-01 到现在的秒数
- ✅ 用
ZoneInfo 处理标准时区(Python 3.9+)
速查表
from datetime import datetime, date, timedelta
# 获取当前
now = datetime.now() # 当前日期+时间
today = date.today() # 当前日期
ts = now.timestamp() # 当前时间戳
# 格式化
now.strftime("%Y-%m-%d %H:%M:%S") # → "2024-01-15 14:30:25"
# 解析
datetime.strptime("2024-01-15", "%Y-%m-%d") # → datetime
# 计算
future = now + timedelta(days=7) # 7天后
past = now - timedelta(hours=2) # 2小时前
diff = (date(2025,1,1) - today).days # 距离天数
🎯 练习建议:
1. 写一个程序,计算从你出生到现在活了多少天、多少小时、多少秒
2. 写一个日程提醒工具:输入日期和事件名,自动显示"还有 X 天"
3. 写一个函数,把各种格式的日期字符串统一转成 "YYYY-MM-DD" 格式
📚 Day30 完成!明天学习 os 与 pathlib —— 文件目录操作一把梭