Skip to content

Python Datetime:Python日期和时间完全指南

Updated on

在Python中处理日期和时间应该很简单,但实际上很少如此。你需要从CSV文件中解析日期字符串,但"01/02/2026"根据地区设置可能表示1月2日或2月1日。你想计算两个时间戳之间的差异,但一个是字符串,另一个是Unix纪元。你这个月已经第十次搞混了strftimestrptime。日期和时间操作是生产代码中bug、偏移错误和时区头痛的持续来源。

后果不仅仅是烦恼。财务报告中错误的日期格式意味着不正确的计算。忽略时区的朴素datetime比较会导致重复的cron任务。错误的timedelta计算会向客户收取31天而不是30天的费用。

Python内置的datetime模块通过一个干净、一致的API解决了这些问题。它提供了日期、时间、时间戳和持续时间的类。一旦你学会了它的模式——特别是strftimestrptime之间的区别——你就可以在不使用第三方库的情况下处理任何日期操作。

📚

获取当前日期和时间

最常见的起点是获取当前日期和时间。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.123456

datetime.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格式代码参考

代码含义示例
%Y4位年份2026
%y2位年份26
%m零填充的月份03
%B月份全名March
%b月份缩写Mar
%d零填充的日期15
%A星期全名Sunday
%a星期缩写Sun
%H小时(24小时制,零填充)09
%I小时(12小时制,零填充)09
%M分钟(零填充)05
%S秒(零填充)07
%pAM/PMAM
%f微秒(零填充至6位)000000
%zUTC偏移(+HHMM或-HHMM)+0000
%Z时区名称UTC
%j年中的第几天(001-366)074
%%字面%字符%

最常用的格式模式

模式格式字符串输出
ISO 8601%Y-%m-%dT%H:%M:%S2026-03-15T09:05:07
美国日期%m/%d/%Y03/15/2026
欧洲日期%d/%m/%Y15/03/2026
可读日期%B %d, %YMarch 15, 2026
日志时间戳%Y-%m-%d %H:%M:%S2026-03-15 09:05:07
12小时制%I:%M:%S %p09:05:07 AM
紧凑日期%Y%m%d20260315
文件安全时间戳%Y%m%d_%H%M%S20260315_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.0

timedelta构造函数参数

参数描述示例
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只存储dayssecondsmicroseconds。其他所有内容都会被转换。

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:快速对比

strftimestrptime
全称String Format TimeString 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)。你也可以使用weekshoursminutesseconds作为参数。减法也同样适用: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)作为参考点开始,仅在向用户显示时才转换为本地时区。

📚