Python datetime 模块实用入门与避坑指南

不管是做爬虫存爬取记录、Web后端处理订单截止/用户生日,还是做数据分析做时间序列,Python里的datetime都是绕不开的标准库神兵利器——不用pip安装,开箱即用!本入门教程就带你快速搞定它的核心功能,顺便提几个新手容易踩的小坑~


核心模块、类梳理

先提个新手最容易混淆的小细节:原模块名是datetime,模块里又嵌套了一个同名类datetime!为了代码清晰,后面的示例都会尽量精准导入需要的类,避免混淆。


获取当前日期时间

最基础的操作,要先获取本地环境(或者指定时区,后面讲)的「当前时刻」。

# 精准导入datetime类(不是导入整个模块!)
from datetime import datetime

# 获取本地当前日期时间(带微秒精度,本地时区无显式标记)
now_local = datetime.now()
print(now_local)  # 示例输出:2023-07-15 14:30:45.123456
print(type(now_local))  # <class 'datetime.datetime'>

自定义创建日期时间

有时候我们不需要「现在」,而是要指定特定的年、月、日,甚至时、分、秒、微秒

from datetime import datetime

# 必传参数:年、月、日
dt_basic = datetime(2023, 7, 15)
print(dt_basic)  # 2023-07-15 00:00:00

# 可选参数(按顺序):时、分、秒、微秒
dt_full = datetime(2023, 7, 15, 14, 30, 59, 987654)
print(dt_full)  # 2023-07-15 14:30:59.987654

与时间戳的双向转换

什么是时间戳?

时间戳是从1970年1月1日00:00:00 UTC+0(格林威治时间) 开始的「秒数」,Python里返回的是带微秒小数的浮点数。它的最大优势是时区无关——不管你在东京还是纽约,同一个瞬间的时间戳完全相同,非常适合存储

datetime → 时间戳

from datetime import datetime

dt = datetime(2023, 7, 15, 14, 30)
ts = dt.timestamp()
print(ts)  # 示例输出:1689409800.0

⚠️ 避坑:本地创建的dt默认用本地时区

如果创建dt时没有显式设置时区,timestamp()会自动用本地时区换算成UTC秒数,这在跨环境运行(比如本地Windows、服务器Linux)时可能出问题!

时间戳 → datetime

有两个常用方法,区别在于返回的是「本地时区」还是「UTC+0时区」:

from datetime import datetime

ts = 1689409800.0

# 方法1:本地时区的datetime(无显式标记)
dt_local = datetime.fromtimestamp(ts)

# 方法2:UTC+0时区的datetime(无显式标记)
dt_utc = datetime.utcfromtimestamp(ts)

print(dt_local)  # 本地时区的2023-07-15 14:30:00
print(dt_utc)    # 如果本地是UTC+8,这里会是2023-07-15 06:30:00

与字符串的双向转换

我们和用户、文件、数据库交互时,最常遇到的不是datetime对象,而是人类可读的字符串,这时候就需要双向转换了。

字符串 → datetime(解析)

strptime()方法,核心是要严格指定格式符,格式符必须和字符串完全匹配,否则会报错!

from datetime import datetime

# 解析ISO标准格式(最常用)
date_iso = "2023-07-15 14:30:00"
dt_iso = datetime.strptime(date_iso, '%Y-%m-%d %H:%M:%S')
print(dt_iso)  # 2023-07-15 14:30:00

# 解析中文自然格式
date_cn = "2023年7月15日 14:30"
dt_cn = datetime.strptime(date_cn, '%Y年%m月%d日 %H:%M')
print(dt_cn)  # 2023-07-15 14:30:00

常用格式符速查

格式符含义示例
%Y4位年份2023
%m2位月份(01-12)07
%d2位日期(01-31)15
%H24小时制小时(00-23)14
%M2位分钟(00-59)30
%S2位秒(00-59)59
%a英文星期缩写Sat
%b英文月份缩写Jul

datetime → 字符串(格式化)

strftime()方法,同样用上面的格式符,还能自定义输出样式:

from datetime import datetime

now = datetime.now()

# 输出人类友好的英文格式
fmt_en = now.strftime('%a, %b %d %Y %H:%M:%S')
print(fmt_en)  # 示例:Sat, Jul 15 2023 14:30:45

# 输出简洁的日志格式
fmt_log = now.strftime('%Y%m%d_%H%M%S')
print(fmt_log)  # 示例:20230715_143045

日期时间的加减计算

要算「3天后的截止日期」「2小时前的爬取时刻」?用timedelta类就对了!它表示两个datetime之间的时间差,支持天、小时、分钟、秒、微秒的加减。

from datetime import datetime, timedelta

now = datetime.now()

# 10小时后
print(now + timedelta(hours=10))

# 1天3小时前
print(now - timedelta(days=1, hours=3))

# 两个datetime相减,得到timedelta对象
dt1 = datetime(2023, 7, 15)
dt2 = datetime(2023, 7, 18)
delta = dt2 - dt1
print(delta.days)  # 3

简单的时区处理

Python 3.2+ 内置了timezone类,配合timedelta就能处理基础的时区问题了!

显式给datetime设置时区

之前避坑说过,默认创建的datetime没有时区标记,我们可以用replace()或者直接在now()里传tzinfo参数:

from datetime import datetime, timezone, timedelta

# 创建UTC+8(北京/上海/香港)的时区对象
tz_utc8 = timezone(timedelta(hours=8))

# 方法1:now()直接传时区
now_utc8 = datetime.now(tz_utc8)
print(now_utc8)  # 示例输出:2023-07-15 14:30:45.123456+08:00

# 方法2:现有dt替换时区(⚠️ 不会自动换算时间!)
dt_local = datetime.now()
dt_utc8_replace = dt_local.replace(tzinfo=tz_utc8)

时区之间的转换

astimezone()方法,前提是原datetime必须有时区标记

from datetime import datetime, timezone, timedelta

# 先获取带UTC+0标记的当前时间
now_utc0 = datetime.now(timezone.utc)

# 转换为东京时间(UTC+9)
tz_utc9 = timezone(timedelta(hours=9))
now_tokyo = now_utc0.astimezone(tz_utc9)
print(now_tokyo)  # 比now_utc0多9小时

最佳实践总结

  1. 存储优先用时间戳/带时区的UTC datetime:完全时区无关,跨环境不会乱
  2. 显示时再转换为用户时区:比如Web端读取用户浏览器时区,后端转好返回
  3. 严格匹配strptime的格式符:哪怕是空格、标点符号都不能错

小练习:带时区字符串转时间戳

整合一下前面的知识,写个小函数:

import re
from datetime import datetime, timezone, timedelta

def to_utc_ts(dt_str, tz_str):
    # 1. 解析日期时间字符串
    dt = datetime.strptime(dt_str, '%Y-%m-%d %H:%M:%S')
    
    # 2. 解析时区字符串(比如UTC+8:00、UTC-5:00)
    tz_pattern = r'^UTC([+-]\d{1,2}):00$'
    tz_match = re.match(tz_pattern, tz_str)
    if not tz_match:
        raise ValueError('请输入正确的时区格式,如UTC+8:00')
    
    # 3. 创建设置时区,转为时间戳
    tz_hours = int(tz_match.group(1))
    tz = timezone(timedelta(hours=tz_hours))
    dt_with_tz = dt.replace(tzinfo=tz)
    return dt_with_tz.timestamp()

# 测试一下
if __name__ == '__main__':
    ts1 = to_utc_ts('2023-7-15 14:30:00', 'UTC+8:00')
    print(ts1)  # 1689409800.0
    ts2 = to_utc_ts('2023-7-15 01:30:00', 'UTC-5:00')
    print(ts2)  # 和ts1一样!因为是同一个瞬间

搞定这些核心功能,日常90%的日期时间需求都能轻松解决啦!