# 用 Python 标准库掌握时间处理:`datetime` 全面攻略 > **系列 03 – 日期/时间运算、时区、格式转换一次搞定** 时间不只是“字符串”。日期会跨日、月份会变、夏令时会插入,且不同地区的基准也不同。 因此在处理日期/时间时,必须区分**可显示的字符串**与**可计算的时间值**,并养成包含时区的习惯。 ![时钟工匠的魔法工作室](/media/editor_temp/6/5843a4cb-f3a6-4d8d-9a13-89c1cbb0ef6c.png) 本文围绕 `datetime` 模块,整理以下内容。 * 创建当前时间与日期 * 计算时间差(`timedelta`) * 字符串格式化/解析(`strftime`、`strptime`) * 时区处理(`timezone`、`zoneinfo`) * 常见**注意点**与稳定模式 --- ## 1. `datetime` 提供了哪些功能? {#sec-5f3cbdfb3d40} `datetime` 包含看似相似但职责不同的类型。 * `date`:仅需要年-月-日 * `time`:仅需要时:分:秒 * `datetime`:日期+时间(最常用) * `timedelta`:时间“差值”(持续时间) * `timezone`:固定偏移时区(如 UTC+9,偏移不变) Python 3.9+ 之后,标准库加入 **`zoneinfo`**,使得处理地区时区(如 Asia/Tokyo)更为简便。 --- ## 2. 如何获取“现在”:从 naive 到 aware 的整理 {#sec-80466bd4f739} ### 2.1 naive / aware 是什么? {#sec-893b0793d467} `datetime` 对象大致分为两类。 * **naive datetime**:没有时区信息 * **aware datetime**:包含时区信息(`tzinfo`) 混用两者进行运算/比较会导致错误,甚至在无错误的情况下得到意外结果。 ### 2.2 推荐的默认值:以 UTC aware 开始 {#sec-f876fdec8f2b} 统一使用 UTC 作为存储/计算基准往往更为整洁。 ```python from datetime import datetime, timezone utc_now = datetime.now(timezone.utc) # aware (UTC) print(utc_now) ``` 需要本地时间时,在**显示阶段**进行转换。 ```python from datetime import datetime, timezone from zoneinfo import ZoneInfo utc_now = datetime.now(timezone.utc) tokyo_now = utc_now.astimezone(ZoneInfo("Asia/Tokyo")) print(utc_now) print(tokyo_now) ``` --- ## 3. 日期/时间计算:`timedelta` 为核心 {#sec-5497d843fd3e} ### 3.1 加减 {#sec-0288780aa297} ```python from datetime import datetime, timedelta, timezone now = datetime.now(timezone.utc) print(now + timedelta(days=3)) print(now - timedelta(hours=2)) ``` ### 3.2 两个时间点的差值 {#sec-c781f333910b} ```python from datetime import datetime, timezone a = datetime(2026, 1, 1, tzinfo=timezone.utc) b = datetime(2026, 1, 10, tzinfo=timezone.utc) delta = b - a print(delta.days) # 9 print(delta.total_seconds()) # 777600.0 ``` ### 3.3 用 `timedelta` 表示“期间”的感知 {#sec-c81173d2a4f5} `timedelta` 以**天/秒/微秒**为单位。 “一个月后”这类因月长变化的表达,`timedelta(days=30)` 只能粗略近似;若需基于日历的“下个月同日”,则需要 `dateutil` 等外部工具。 --- ## 4. 格式化与解析:`strftime` / `strptime` {#sec-a17a8e259a48} ### 4.1 datetime → 字符串(`strftime`) {#sec-8d6f34025b2e} ```python from datetime import datetime, timezone dt = datetime(2026, 1, 30, 14, 5, 0, tzinfo=timezone.utc) print(dt.strftime("%Y-%m-%d %H:%M:%S %z")) ``` 常用格式模式: * `%Y-%m-%d` : 2026-01-30 * `%H:%M:%S` : 14:05:00 * `%z` : +0000(UTC 偏移) ### 4.2 字符串 → datetime(`strptime`) {#sec-f2bbc1672485} ```python from datetime import datetime s = "2026-01-30 14:05:00" dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S") print(dt) ``` 此处得到的 `dt` 为 **naive**。若想明确时区,需要附加 `tzinfo`。 ```python from datetime import datetime, timezone s = "2026-01-30 14:05:00" dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc) print(dt) ``` > `replace(tzinfo=...)` 只是给时间点贴上时区标签,并不做时区转换。 > 若需转换,请使用 `astimezone()`(见下节)。 --- ## 5. 时区:区分 `timezone` 与 `zoneinfo` 的使用 {#sec-05b1d0f7ef82} ### 5.1 固定偏移时区使用 `timezone` {#sec-67fec65074ab} 示例:UTC、UTC+9 等偏移固定的时区。 ```python from datetime import datetime, timezone, timedelta kst_fixed = timezone(timedelta(hours=9)) dt = datetime(2026, 1, 30, 12, 0, tzinfo=kst_fixed) print(dt) ``` ### 5.2 地区时区使用 `zoneinfo` {#sec-834b663bce83} 夏令时等偏移变化的地区适合使用 `zoneinfo`。 ```python from datetime import datetime, timezone from zoneinfo import ZoneInfo utc_now = datetime.now(timezone.utc) ny_now = utc_now.astimezone(ZoneInfo("America/New_York")) print(ny_now) ``` ### 5.3 `replace(tzinfo=...)` 与 `astimezone(...)` 的区别 {#sec-35bf0fbaa9df} * `replace(tzinfo=...)`:**时间值不变**,仅给时区贴标签 * `astimezone(...)`:**同一时刻**在不同时区的时间表示 了解这点可大幅减少时区相关错误。 --- ## 6. 常见注意点汇总 {#sec-552ca3933648} ### 6.1 naive/aware 混用 {#sec-21a4c96b9022} * 内部计算时间建议使用 **aware(UTC)**,更稳健。 * 外部输入时,立即确定时区并统一。 ### 6.2 “本地时间”受环境影响 {#sec-e2bd08b413c3} `datetime.now()` 取决于执行环境的本地设置。容器/服务器可能是 UTC,也可能不是。 使用 `datetime.now(timezone.utc)` 更可靠。 ### 6.3 字符串解析的格式不匹配 {#sec-e52b753858ac} `strptime` 对格式极为严格。若输入格式多样,需先做预处理或逐步尝试多种格式(最好限制输入格式)。 --- ## 7. 三大常用模式 {#sec-b244af674a6a} ### 7.1 以 ISO 8601 存储 {#sec-4b1979b29466} 标准字符串表示可用 `isoformat()`。 ```python from datetime import datetime, timezone dt = datetime.now(timezone.utc) print(dt.isoformat()) # 例如: 2026-01-30T05:12:34.567890+00:00 ``` ### 7.2 用“今天日期”生成文件名 {#sec-ae13a5dac423} ```python from datetime import datetime stamp = datetime.now().strftime("%Y%m%d") filename = f"report_{stamp}.json" print(filename) ``` ### 7.3 计算到某个时间点的剩余时间 {#sec-b509d1188ff7} ```python from datetime import datetime, timezone target = datetime(2026, 2, 1, 0, 0, tzinfo=timezone.utc) now = datetime.now(timezone.utc) remaining = target - now print(remaining) print(remaining.total_seconds()) ``` --- ## 8. 结语 {#sec-9fa53305f95a} `datetime` 不仅能生成日期字符串,更能把时间变成可运算的值。结合时区(`timezone`、`zoneinfo`),即使运行环境不同,也能写出稳定的代码。 下一篇将单独讨论 `random`,涵盖随机数生成/采样/洗牌/种子以及安全随机数(`secrets`)等。 --- **相关文章:** - [Django 中正确使用 datetime 与 timezone](/ko/whitedec/2025/11/10/django-datetime-timezone/) - [Django 的时间管理魔法 - `django.utils.timezone` 完全指南](/ko/whitedec/2025/11/14/django-utils-timezone-guide/) --- **查看前置系列** - [[Python 标准库 - 0] Python 标准库是什么?面向初学者的指南](/ko/whitedec/2026/1/29/python-standard-library/) - [[Python 标准库 -1] 文件系统 & OS 环境掌握:pathlib vs os](/ko/whitedec/2026/1/29/file-system-os-environment-master-pathlib-vs-os/) - [[Python 标准库 - 2] 数据存储 & 序列化:json, pickle, csv](/ko/whitedec/2026/1/30/python-json-pickle-csv/)