# 使用 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 vs 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=...)` vs `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/)