Python Datetime:Python日期和时间完全指南
Updated on
在Python中处理日期和时间应该很简单,但实际上很少如此。你需要从CSV文件中解析日期字符串,但"01/02/2026"根据地区设置可能表示1月2日或2月1日。你想计算两个时间戳之间的差异,但一个是字符串,另一个是Unix纪元。你这个月已经第十次搞混了strftime和strptime。日期和时间操作是生产代码中bug、偏移错误和时区头痛的持续来源。
后果不仅仅是烦恼。财务报告中错误的日期格式意味着不正确的计算。忽略时区的朴素datetime比较会导致重复的cron任务。错误的timedelta计算会向客户收取31天而不是30天的费用。
Python内置的datetime模块通过一个干净、一致的API解决了这些问题。它提供了日期、时间、时间戳和持续时间的类。一旦你学会了它的模式——特别是strftime和strptime之间的区别——你就可以在不使用第三方库的情况下处理任何日期操作。
获取当前日期和时间
最常见的起点是获取当前日期和时间。datetime模块提供了几种方法来实现。
from datetime import datetime, date
# 当前日期和时间
now = datetime.now()
print(now) # 2026-02-10 14:30:45.123456
# 仅当前日期
today = date.today()
print(today) # 2026-02-10
# datetime.today() 类似于 datetime.now() 但不支持时区
now_alt = datetime.today()
print(now_alt) # 2026-02-10 14:30:45.123456datetime.now()和datetime.today()之间的区别微妙但重要。datetime.now()接受一个可选的tz参数用于时区感知的datetime。datetime.today()不接受。对于大多数代码,优先使用datetime.now()。
from datetime import datetime
from zoneinfo import ZoneInfo
# 时区感知的当前时间
utc_now = datetime.now(tz=ZoneInfo("UTC"))
print(utc_now) # 2026-02-10 14:30:45.123456+00:00
tokyo_now = datetime.now(tz=ZoneInfo("Asia/Tokyo"))
print(tokyo_now) # 2026-02-10 23:30:45.123456+09:00创建Datetime对象
你可以从单独的组件或现有数据创建datetime对象。
from datetime import datetime, date, time
# 从年、月、日、时、分、秒创建
dt = datetime(2026, 3, 15, 9, 30, 0)
print(dt) # 2026-03-15 09:30:00
# 仅日期
d = date(2026, 12, 25)
print(d) # 2026-12-25
# 仅时间
t = time(14, 30, 0)
print(t) # 14:30:00
# 组合日期和时间
combined = datetime.combine(d, t)
print(combined) # 2026-12-25 14:30:00你也可以从现有的datetime中提取组件。
from datetime import datetime
dt = datetime(2026, 3, 15, 9, 30, 45)
print(dt.year) # 2026
print(dt.month) # 3
print(dt.day) # 15
print(dt.hour) # 9
print(dt.minute) # 30
print(dt.second) # 45
print(dt.weekday()) # 6 (星期日,星期一=0)
print(dt.isoformat()) # 2026-03-15T09:30:45使用strftime格式化日期
strftime代表"string format time"(字符串格式化时间)。它将datetime对象转换为格式化的字符串。你传入一个包含指令的格式字符串,这些指令会被日期组件替换。
from datetime import datetime
dt = datetime(2026, 3, 15, 9, 5, 7)
# 常见格式
print(dt.strftime("%Y-%m-%d")) # 2026-03-15
print(dt.strftime("%d/%m/%Y")) # 15/03/2026
print(dt.strftime("%B %d, %Y")) # March 15, 2026
print(dt.strftime("%Y-%m-%d %H:%M:%S")) # 2026-03-15 09:05:07
print(dt.strftime("%I:%M %p")) # 09:05 AM
print(dt.strftime("%A, %B %d, %Y")) # Sunday, March 15, 2026快速记忆方法:strftime = string from time(datetime转字符串)。
strftime格式代码参考
| 代码 | 含义 | 示例 |
|---|---|---|
%Y | 4位年份 | 2026 |
%y | 2位年份 | 26 |
%m | 零填充的月份 | 03 |
%B | 月份全名 | March |
%b | 月份缩写 | Mar |
%d | 零填充的日期 | 15 |
%A | 星期全名 | Sunday |
%a | 星期缩写 | Sun |
%H | 小时(24小时制,零填充) | 09 |
%I | 小时(12小时制,零填充) | 09 |
%M | 分钟(零填充) | 05 |
%S | 秒(零填充) | 07 |
%p | AM/PM | AM |
%f | 微秒(零填充至6位) | 000000 |
%z | UTC偏移(+HHMM或-HHMM) | +0000 |
%Z | 时区名称 | UTC |
%j | 年中的第几天(001-366) | 074 |
%% | 字面%字符 | % |
最常用的格式模式
| 模式 | 格式字符串 | 输出 |
|---|---|---|
| ISO 8601 | %Y-%m-%dT%H:%M:%S | 2026-03-15T09:05:07 |
| 美国日期 | %m/%d/%Y | 03/15/2026 |
| 欧洲日期 | %d/%m/%Y | 15/03/2026 |
| 可读日期 | %B %d, %Y | March 15, 2026 |
| 日志时间戳 | %Y-%m-%d %H:%M:%S | 2026-03-15 09:05:07 |
| 12小时制 | %I:%M:%S %p | 09:05:07 AM |
| 紧凑日期 | %Y%m%d | 20260315 |
| 文件安全时间戳 | %Y%m%d_%H%M%S | 20260315_090507 |
使用strptime解析字符串
strptime代表"string parse time"(字符串解析时间)。它是strftime的反向操作——将字符串转换为datetime对象。你提供字符串及其对应的格式。
from datetime import datetime
# 解析各种日期字符串格式
dt1 = datetime.strptime("2026-03-15", "%Y-%m-%d")
print(dt1) # 2026-03-15 00:00:00
dt2 = datetime.strptime("15/03/2026", "%d/%m/%Y")
print(dt2) # 2026-03-15 00:00:00
dt3 = datetime.strptime("March 15, 2026 09:30 AM", "%B %d, %Y %I:%M %p")
print(dt3) # 2026-03-15 09:30:00
dt4 = datetime.strptime("2026-03-15T09:30:00", "%Y-%m-%dT%H:%M:%S")
print(dt4) # 2026-03-15 09:30:00记住:strptime = string parse time(字符串转datetime)。
处理解析错误
如果字符串不匹配格式,Python会引发ValueError。在解析用户输入或外部数据时,始终将strptime调用包装在错误处理中。
from datetime import datetime
def safe_parse_date(date_string, fmt="%Y-%m-%d"):
"""Parse a date string safely, returning None on failure."""
try:
return datetime.strptime(date_string, fmt)
except ValueError as e:
print(f"Could not parse '{date_string}': {e}")
return None
# Valid input
print(safe_parse_date("2026-03-15")) # 2026-03-15 00:00:00
# Invalid input
print(safe_parse_date("15-03-2026")) # Could not parse '15-03-2026': ...
print(safe_parse_date("not a date")) # Could not parse 'not a date': ...解析多种格式
当你收到不可预测格式的日期时,尝试多种模式。
from datetime import datetime
def parse_flexible_date(date_string):
"""Try multiple date formats and return the first match."""
formats = [
"%Y-%m-%d",
"%d/%m/%Y",
"%m/%d/%Y",
"%B %d, %Y",
"%b %d, %Y",
"%Y-%m-%dT%H:%M:%S",
"%Y-%m-%d %H:%M:%S",
]
for fmt in formats:
try:
return datetime.strptime(date_string, fmt)
except ValueError:
continue
raise ValueError(f"No matching format found for '{date_string}'")
print(parse_flexible_date("2026-03-15")) # 2026-03-15 00:00:00
print(parse_flexible_date("March 15, 2026")) # 2026-03-15 00:00:00
print(parse_flexible_date("15/03/2026")) # 2026-03-15 00:00:00使用timedelta进行日期运算
timedelta类表示一个持续时间——两个日期或时间之间的差异。你可以添加或减去timedelta对象来将日期向前或向后移动。
from datetime import datetime, timedelta
now = datetime(2026, 2, 10, 12, 0, 0)
# 添加天数
tomorrow = now + timedelta(days=1)
print(tomorrow) # 2026-02-11 12:00:00
# 减去天数
last_week = now - timedelta(weeks=1)
print(last_week) # 2026-02-03 12:00:00
# 添加小时和分钟
later = now + timedelta(hours=5, minutes=30)
print(later) # 2026-02-10 17:30:00
# 组合多个单位
future = now + timedelta(weeks=2, days=3, hours=6)
print(future) # 2026-02-27 18:00:00计算时间差
从一个datetime减去另一个会返回timedelta。
from datetime import datetime
start = datetime(2026, 1, 1)
end = datetime(2026, 12, 31)
diff = end - start
print(diff) # 364 days, 0:00:00
print(diff.days) # 364
print(diff.total_seconds()) # 31449600.0timedelta构造函数参数
| 参数 | 描述 | 示例 |
|---|---|---|
weeks | 周数 | timedelta(weeks=2) = 14天 |
days | 天数 | timedelta(days=30) |
hours | 小时数 | timedelta(hours=12) |
minutes | 分钟数 | timedelta(minutes=45) |
seconds | 秒数 | timedelta(seconds=120) |
milliseconds | 毫秒数 | timedelta(milliseconds=500) |
microseconds | 微秒数 | timedelta(microseconds=1000) |
所有参数都可以组合。在内部,timedelta只存储days、seconds和microseconds。其他所有内容都会被转换。
from datetime import timedelta
delta = timedelta(weeks=1, days=2, hours=3, minutes=30, seconds=45)
print(delta) # 9 days, 3:30:45
print(delta.days) # 9
print(delta.seconds) # 12645 (3*3600 + 30*60 + 45)
print(delta.total_seconds()) # 790245.0比较日期
Datetime对象支持所有标准比较运算符。这使得排序和过滤日期变得简单。
from datetime import datetime
dt1 = datetime(2026, 1, 1)
dt2 = datetime(2026, 6, 15)
dt3 = datetime(2026, 12, 31)
print(dt1 < dt2) # True
print(dt3 > dt2) # True
print(dt1 == dt2) # False
print(dt1 != dt2) # True排序日期
from datetime import datetime
dates = [
datetime(2026, 12, 25),
datetime(2026, 1, 1),
datetime(2026, 7, 4),
datetime(2026, 2, 14),
]
sorted_dates = sorted(dates)
for d in sorted_dates:
print(d.strftime("%B %d, %Y"))
# January 01, 2026
# February 14, 2026
# July 04, 2026
# December 25, 2026使用时间戳
Unix时间戳表示自1970年1月1日(Unix纪元)以来的秒数。datetime模块可以在时间戳和datetime对象之间进行转换。
from datetime import datetime, timezone
# Datetime转时间戳
dt = datetime(2026, 3, 15, 9, 30, 0)
ts = dt.timestamp()
print(ts) # 1773814200.0 (取决于本地时区)
# 时间戳转Datetime(时区感知,推荐)
dt_aware = datetime.fromtimestamp(ts, tz=timezone.utc)
print(dt_aware) # 2026-03-15 01:30:00+00:00重要: 不带时区的datetime.fromtimestamp()返回本地时间。要获取UTC,始终传入tz=timezone.utc。
时区处理
朴素datetime(没有时区信息)是bug的常见来源。Python提供了两种内置方法来处理时区感知的datetime。
使用datetime.timezone(内置)
timezone类处理固定的UTC偏移。
from datetime import datetime, timezone, timedelta
# UTC
utc_now = datetime.now(timezone.utc)
print(utc_now) # 2026-02-10 14:30:00+00:00
# 固定偏移(例如印度的UTC+5:30)
ist = timezone(timedelta(hours=5, minutes=30))
india_time = datetime.now(ist)
print(india_time) # 2026-02-10 20:00:00+05:30
# 在时区之间转换
utc_time = datetime(2026, 3, 15, 12, 0, 0, tzinfo=timezone.utc)
eastern = timezone(timedelta(hours=-5))
eastern_time = utc_time.astimezone(eastern)
print(eastern_time) # 2026-03-15 07:00:00-05:00使用zoneinfo(Python 3.9+)
对于需要正确处理夏令时的命名时区,使用zoneinfo模块。
from datetime import datetime
from zoneinfo import ZoneInfo
# 命名时区
utc = ZoneInfo("UTC")
eastern = ZoneInfo("America/New_York")
tokyo = ZoneInfo("Asia/Tokyo")
# 创建时区感知的datetime
dt = datetime(2026, 7, 15, 12, 0, 0, tzinfo=utc)
print(dt) # 2026-07-15 12:00:00+00:00
# 转换到其他时区
print(dt.astimezone(eastern)) # 2026-07-15 08:00:00-04:00 (EDT)
print(dt.astimezone(tokyo)) # 2026-07-15 21:00:00+09:00朴素Datetime与感知Datetime
| 特征 | 朴素 | 感知 |
|---|---|---|
| 有时区信息 | 否 | 是 |
| 跨时区比较安全 | 否 | 是 |
由datetime.now()创建 | 是 | 否(除非传入tz) |
| 可在运算中混合 | 仅与其他朴素的 | 仅与其他感知的 |
| 推荐用于生产环境 | 否 | 是 |
你不能比较或用朴素datetime减去感知datetime。Python会引发TypeError。
实际示例
计算一个人的年龄
from datetime import date
def calculate_age(birth_date):
"""Calculate age in years from a birth date."""
today = date.today()
age = today.year - birth_date.year
if (today.month, today.day) < (birth_date.month, birth_date.day):
age -= 1
return age
birthday = date(1995, 8, 20)
print(f"Age: {calculate_age(birthday)} years") # Age: 30 years (as of Feb 2026)生成日期范围
from datetime import date, timedelta
def date_range(start, end, step_days=1):
"""Generate dates from start to end (inclusive)."""
current = start
while current <= end:
yield current
current += timedelta(days=step_days)
start = date(2026, 2, 1)
end = date(2026, 2, 7)
for d in date_range(start, end):
print(d.strftime("%A, %B %d"))计算两个日期之间的工作日数
from datetime import date, timedelta
def business_days_between(start, end):
"""Count weekdays (Mon-Fri) between two dates, excluding endpoints."""
count = 0
current = start + timedelta(days=1)
while current < end:
if current.weekday() < 5: # 0=Mon, 4=Fri
count += 1
current += timedelta(days=1)
return count
start = date(2026, 2, 1)
end = date(2026, 2, 28)
print(f"Business days: {business_days_between(start, end)}") # Business days: 19在Jupyter中实验Datetime
日期和时间操作受益于交互式实验。当你从CSV解析不一致的日期格式或调试时区转换时,能够在notebook单元格中测试每个步骤可以节省大量时间。
RunCell (opens in a new tab)是一个直接在Jupyter notebook中工作的AI代理。它可以检查你的datetime对象,为你的数据建议正确的strftime/strptime格式代码,并帮助实时调试时区转换问题。
strftime vs strptime:快速对比
| strftime | strptime | |
|---|---|---|
| 全称 | String Format Time | String Parse Time |
| 方向 | datetime -> 字符串 | 字符串 -> datetime |
| 调用对象 | datetime对象 | datetime类 |
| 语法 | dt.strftime("%Y-%m-%d") | datetime.strptime(s, "%Y-%m-%d") |
| 返回 | 格式化的字符串 | datetime对象 |
| 异常 | 从不 | 格式不匹配时引发ValueError |
| 使用场景 | 显示、日志、文件名 | 解析CSV、API响应、用户输入 |
from datetime import datetime
# strftime: datetime -> string
dt = datetime(2026, 3, 15, 9, 30)
formatted = dt.strftime("%B %d, %Y at %I:%M %p")
print(formatted) # March 15, 2026 at 09:30 AM
# strptime: string -> datetime
parsed = datetime.strptime("March 15, 2026 at 09:30 AM", "%B %d, %Y at %I:%M %p")
print(parsed) # 2026-03-15 09:30:00常见问题
如何在Python中获取当前日期和时间?
使用datetime模块的datetime.now()。仅获取日期使用date.today()。要获取时区感知的当前时间,传入时区:datetime.now(tz=timezone.utc)。这些是标准方法,不需要第三方库。
Python中strftime和strptime的区别是什么?
strftime将datetime对象转换为格式化的字符串(string from time)。strptime解析字符串并将其转换为datetime对象(string parse time)。记住:strftime输出字符串,strptime输入字符串。两者都使用相同的格式代码,如%Y、%m、%d。
如何在Python中给日期添加天数?
使用timedelta类。从datetime导入,然后添加到你的日期:new_date = old_date + timedelta(days=7)。你也可以使用weeks、hours、minutes和seconds作为参数。减法也同样适用:past_date = today - timedelta(days=30)。
如何在Python中将字符串转换为datetime?
使用datetime.strptime(string, format)。你需要提供与输入匹配的格式字符串。例如,datetime.strptime("2026-03-15", "%Y-%m-%d")解析ISO日期。如果字符串不匹配格式,Python会引发ValueError,所以对于外部数据,将调用包装在try/except块中。
如何在Python datetime中处理时区?
对于Python 3.9+,使用内置的zoneinfo模块:from zoneinfo import ZoneInfo。使用datetime.now(tz=ZoneInfo("UTC"))创建时区感知的datetime,使用dt.astimezone(ZoneInfo("America/New_York"))在时区之间转换。对于固定UTC偏移,使用datetime.timezone(timedelta(hours=N))。在生产代码中避免使用朴素datetime。
总结
Python的datetime模块提供了日期和时间操作所需的一切:创建日期、格式化用于显示、从字符串解析、执行运算和处理时区。一旦学会,核心模式是一致且可预测的。
记住核心区别:strftime将datetime格式化为字符串,strptime将字符串解析为datetime。使用timedelta进行日期运算。使用zoneinfo(Python 3.9+)进行正确的时区处理。在生产代码中保持datetime的时区感知,以避免微妙的比较bug。
对于大多数项目,内置的datetime模块就足够了。在现代Python中你不需要pytz——zoneinfo原生处理命名时区。以datetime.now(tz=timezone.utc)作为参考点开始,仅在向用户显示时才转换为本地时区。