使用 Python 標準函式庫掌握時間:完整攻略 datetime

系列 03 – 日期/時間運算、時區、格式轉換一次搞定

時間不只是「字串」。日期會跨日、月份會變、夏令時間會插入,且各地區的基準不同。 因此在處理日期/時間時,必須區分「顯示用字串」與「可計算的時間值」,並習慣包含時區的處理。

時鐘工匠的魔法工作室

本文以 datetime 模組為核心,整理以下內容。

  • 建立現在的時間與日期
  • 計算期間(timedelta
  • 字串格式化/解析(strftimestrptime
  • 時區處理(timezonezoneinfo
  • 常見 注意點 與穩定模式

1. datetime 提供了什麼?



datetime 包含看似相似但功能不同的型別。

  • date:僅需年-月-日
  • time:僅需時:分:秒
  • datetime:日期+時間(最常用)
  • timedelta:時間「差距」或「期間」
  • timezone:固定偏移時區(如 UTC+9)

Python 3.9+ 以後,標準函式庫加入 zoneinfo,讓處理區域時區(如 Asia/Tokyo)更簡單。


2. 如何取得「現在」:naive vs aware 的整理

2.1 naive / aware 是什麼?

datetime 物件大致分為兩類。

  • naive datetime:沒有時區資訊
  • aware datetime:帶有時區資訊(tzinfo

混用兩者進行運算或比較,可能會產生錯誤,或在不報錯的情況下得到意外結果。

2.2 推薦的預設值:以 UTC aware 開始

統一以 UTC 為基準,能讓儲存與計算更乾淨。

from datetime import datetime, timezone

utc_now = datetime.now(timezone.utc)  # aware (UTC)
print(utc_now)

若需要本地時間,於「顯示階段」再做轉換。

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 為核心



3.1 加減

from datetime import datetime, timedelta, timezone

now = datetime.now(timezone.utc)
print(now + timedelta(days=3))
print(now - timedelta(hours=2))

3.2 兩個時間點的差距

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 表示「期間」的感覺

timedelta天/秒/微秒 為單位。 「一個月後」這類因月份長度不同而變化的表達,若用 timedelta(days=30) 只能粗略估算;若要「下個月同一天」則需其他工具(如 dateutil)。


4. 格式化與解析:strftime / strptime

4.1 datetime → 字串 (strftime)

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)

from datetime import datetime

s = "2026-01-30 14:05:00"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
print(dt)

此處產生的 dtnaive。若要明確時區,需加上 tzinfo

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. 時區:區分 timezonezoneinfo 的使用

5.1 固定偏移時區使用 timezone

例如 UTC、UTC+9 等固定偏移。

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

夏令時間等會變動的時區,請使用 zoneinfo

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(...) 的差異

  • replace(tzinfo=...)時間值不變,僅貼上時區標籤
  • astimezone(...)同一時刻轉換為另一時區的時間

了解此差異,可大幅減少時區相關錯誤。


6. 常見注意點整理

6.1 naive/aware 混用

  • 內部計算時間建議使用 aware(UTC),更穩定
  • 外部輸入時,立即確定時區並統一

6.2 「本地時間」受環境影響

datetime.now() 會依執行環境的本地設定。容器或伺服器環境可能是 UTC,也可能不是。 若想消除差異,請使用 datetime.now(timezone.utc)

6.3 字串解析格式不一致

strptime 只要格式不符就會失敗。若輸入格式多樣,需先做「前處理」或逐步嘗試多種格式(或盡量限制輸入格式)。


7. 常用三大模式

7.1 以 ISO 8601 形式儲存

若需要標準字串表示,isoformat() 非常方便。

from datetime import datetime, timezone

dt = datetime.now(timezone.utc)
print(dt.isoformat())  # 例:2026-01-30T05:12:34.567890+00:00

7.2 用「今天日期」作檔名

from datetime import datetime

stamp = datetime.now().strftime("%Y%m%d")
filename = f"report_{stamp}.json"
print(filename)

7.3 計算到特定時間的剩餘時間

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. 結語

datetime 不僅能「產生日期字串」,更能把時間視為可運算的值。結合時區(timezonezoneinfo),即使執行環境不同,也能寫出穩定的程式。

下一篇將獨立討論 random,涵蓋隨機數產生、抽樣、洗牌、種子與安全隨機數(secrets)等。


相關文章:


系列前作: