CTYPE html> Python Datetime & Time 完整教程
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
%H24小时制小时(00-23)14
%I12小时制小时(01-12)02
%M分钟(00-59)30
%S秒(00-59)45
%f微秒123456
%A星期全称Wednesday
%B月份全称May
%pAM/PMPM
%j年内第几天147
%Z时区名称CST
%zUTC 偏移+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

索引属性说明范围
0tm_year如 2026
1tm_mon1-12
2tm_mday1-31
3tm_hour0-23
4tm_min0-59
5tm_sec0-60
6tm_wday星期几0-6
7tm_yday年内第几天1-366
8tm_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秒