Python 日期與時間(Datetime)
date、time、timedelta 完全攻略
簡介
在日常開發中,日期與時間是不可或缺的資訊。無論是記錄使用者的登入時間、計算兩筆訂單之間的間隔,或是產生報表的時間戳,都需要正確且彈性的時間處理。Python 內建的 datetime 模組提供了 date、time、timedelta 等核心類別,讓開發者可以以直觀且安全的方式操作時間資料。
本篇文章將從 概念說明、實作範例、常見陷阱 以及 最佳實踐 全面切入,協助初學者快速上手,同時提供給中級開發者在實務專案中可直接套用的技巧。
核心概念
1. datetime.date – 僅表示「日期」
date 只包含年、月、日三個屬性,沒有時、分、秒的資訊。適合用於 生日、節假日、會議日期 等只關心「哪一天」的情境。
from datetime import date
# 建立日期物件(年, 月, 日)
today = date.today()
print("今天日期:", today) # 2025-11-20
# 指定日期
birthday = date(1990, 5, 15)
print("生日:", birthday) # 1990-05-15
# 取得各屬性
print("年份:", today.year) # 2025
print("月份:", today.month) # 11
print("日期:", today.day) # 20
2. datetime.time – 僅表示「時間」
time 用來描述 時、分、秒、微秒,不包含日期資訊。常見於 排程系統、時段設定 等只需要時間點的情況。
from datetime import time
# 建立時間物件(時, 分, 秒, 微秒)
t1 = time(14, 30) # 14:30:00
t2 = time(9, 45, 12, 500000) # 09:45:12.500000
print("上午時段:", t1) # 14:30:00
print("精確時間:", t2) # 09:45:12.500000
# 取得屬性
print("小時:", t2.hour) # 9
print("分鐘:", t2.minute) # 45
print("秒數:", t2.second) # 12
print("微秒:", t2.microsecond) # 500000
3. datetime.timedelta – 時間差距
timedelta 代表兩個時間點之間的 時間差,支援天、秒、微秒等單位的加減運算。它是 日期/時間算術 的核心。
from datetime import timedelta, date
# 建立 timedelta(天, 秒, 微秒)
delta1 = timedelta(days=10) # 10 天
delta2 = timedelta(hours=5, minutes=30) # 5 小時 30 分鐘
# 日期加上時間差
future = date.today() + delta1
print("10 天後的日期:", future)
# 時間差轉換為秒
print("5 小時 30 分鐘等於秒數:", delta2.total_seconds())
4. datetime.datetime – 完整的「日期+時間」
雖然本主題重點是 date、time、timedelta,但在實務上,我們常會先建立 datetime,再透過 .date() 或 .time() 取得子集。
from datetime import datetime
now = datetime.now()
print("現在時間:", now) # 2025-11-20 13:45:23.123456
# 分離出日期與時間
just_date = now.date()
just_time = now.time()
print("只要日期:", just_date) # 2025-11-20
print("只要時間:", just_time) # 13:45:23.123456
程式碼範例(實用案例)
範例 1️⃣ 計算兩筆交易的天數差
from datetime import date
order1 = date(2025, 10, 1)
order2 = date(2025, 11, 15)
days_between = (order2 - order1).days
print(f"兩筆訂單相差 {days_between} 天") # 兩筆訂單相差 45 天
說明:直接用
date物件相減會得到timedelta,再取.days取得天數。
範例 2️⃣ 產生每週例行任務的時間點(每週一 09:00)
from datetime import datetime, timedelta, time
def next_monday_at_9(now=None):
now = now or datetime.now()
# 找到本週的星期一
monday = now - timedelta(days=now.weekday())
target = datetime.combine(monday.date(), time(9, 0))
# 若已過本週 9 點,則往後推一週
if target <= now:
target += timedelta(weeks=1)
return target
print("下次例行任務時間:", next_monday_at_9())
說明:利用
timedelta(days=now.weekday())取得本週一,再以datetime.combine合併日期與時間,最後判斷是否已過。
範例 3️⃣ 計算「倒數計時」:距離活動還有多少時間
from datetime import datetime, timedelta
event = datetime(2025, 12, 31, 23, 59, 59)
now = datetime.now()
remaining = event - now
print(f"距離跨年還有 {remaining.days} 天 "
f"{remaining.seconds // 3600} 小時 "
f"{(remaining.seconds % 3600) // 60} 分鐘")
說明:
timedelta內的seconds只包含不到一天的秒數,需自行換算成時、分。
範例 4️⃣ 時區感知(timezone-aware)與 date/time 的互換
from datetime import datetime, timezone, timedelta, date, time
# 建立 UTC 時間
utc_now = datetime.now(timezone.utc)
# 轉換為台北時間 (+8 時區)
tz_tpe = timezone(timedelta(hours=8))
tpe_now = utc_now.astimezone(tz_tpe)
print("UTC 現在:", utc_now)
print("台北現在:", tpe_now)
# 只取日期與時間
tpe_date = tpe_now.date()
tpe_time = tpe_now.time()
print("台北日期:", tpe_date)
print("台北時間:", tpe_time)
說明:
datetime支援時區資訊,date與time本身是 無時區 的,若需要時區概念,請先在datetime上處理。
範例 5️⃣ 使用 timedelta 做排程批次的「間隔」計算
from datetime import datetime, timedelta
start = datetime(2025, 11, 20, 8, 0) # 批次開始時間 08:00
interval = timedelta(minutes=15) # 每 15 分鐘執行一次
batch_times = [start + i * interval for i in range(5)]
for idx, t in enumerate(batch_times, 1):
print(f"第 {idx} 次執行時間: {t.time()}")
輸出
第 1 次執行時間: 08:00:00
第 2 次執行時間: 08:15:00
第 3 次執行時間: 08:30:00
第 4 次執行時間: 08:45:00
第 5 次執行時間: 09:00:00
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
直接比較 naive datetime 與 aware datetime |
兩者混用會拋出 TypeError。 |
盡量使用 時區感知 (timezone-aware) 的 datetime,或在比較前先 replace(tzinfo=None)。 |
| 忽略閏年與月份天數 | 手動加減天數時可能出錯(例如 2 月 28 日加 1 天)。 | 使用 timedelta 或 dateutil.relativedelta 讓程式自行處理。 |
timedelta 只支援天、秒、微秒 |
想直接使用「月」或「年」會失敗。 | 若需要月、年,使用 dateutil.relativedelta 或自行計算。 |
| 時間字串格式不一致 | strptime 失敗會導致例外。 |
統一使用 ISO 8601 (YYYY-MM-DDTHH:MM:SS) 或在解析前先驗證。 |
| 忘記考慮夏令時間(DST) | 時區轉換在夏令時間切換時會產生錯誤。 | 使用 pytz 或 Python 3.9+ 的 zoneinfo,讓系統自動處理 DST。 |
最佳實踐
- 盡量使用
datetime取代單獨的date/time:在需要同時處理日期與時間時,直接使用datetime可避免不必要的轉換。 - 統一時區:在大型系統中,建議所有時間均以 UTC 存儲,顯示層再轉換為使用者時區。
- 使用
timedelta進行算術:避免自行計算秒、分、時的進位,交給timedelta處理更安全。 - 封裝時間工具函式:將常用的「取得本週一」或「倒數計時」等功能抽成函式,提升程式碼可讀性與重用性。
實際應用場景
| 場景 | 需求 | 使用的 datetime 組件 |
|---|---|---|
| 訂單系統 | 計算訂單出貨天數、逾期時間 | date、timedelta |
| 排程任務 | 每天 02:00 自動備份資料庫 | datetime.combine + time + timedelta |
| 社群平台 | 顯示「X 分鐘前」的發文時間 | datetime.now()、timedelta、.total_seconds() |
| 金融交易 | 以毫秒為單位的時間戳記 | datetime + timestamp() |
| 多時區報表 | 產生跨國營運的每日營收 | datetime + timezone ( zoneinfo ) |
總結
date、time、timedelta是 Python 日期時間處理的基石,分別負責「只要日期」「只要時間」以及「時間差」的表示。- 透過
datetime的組合與timedelta的算術,我們可以輕鬆完成 日期加減、時間間隔、排程計算 等常見需求。 - 注意 時區感知、閏年/月份天數、夏令時間 等陷阱,遵循 統一時區、封裝工具函式 的最佳實踐,可讓程式碼更健壯、易維護。
掌握了這些概念與範例後,你就能在任何 Python 專案中自信地處理日期與時間,從簡單的生日提醒到複雜的跨時區排程,都能迎刃而解。祝你寫程式愉快,時間永遠在你掌控之中!