本文 AI 產出,尚未審核

MySQL 日期時間型別:DATE、DATETIME 與 TIMESTAMP 完全指南


簡介

在資料庫設計裡,時間資訊往往是最關鍵的欄位,無論是訂單建立時間、使用者最後登入時間,還是報表統計的區間,都離不開正確的日期與時間儲存。MySQL 提供了多種日期時間型別,其中最常用的三個是 DATE、DATETIMETIMESTAMP。它們看似相似,實際上在儲存範圍、時區處理、預設值與效能上都有顯著差異。了解這些差異,才能避免資料錯誤、查詢效能下降,甚至產生跨時區的混亂。

本篇文章針對 初學者到中級開發者,以淺顯易懂的語言說明三種型別的特性、使用時機與常見陷阱,並提供實務範例,協助你在 MySQL 中正確、有效率地處理日期時間資料。


核心概念

1. DATE

屬性 說明
儲存格式 YYYY‑MM‑DD(僅包含日期)
儲存空間 3 bytes
支援範圍 1000‑01‑01 ~ 9999‑12‑31
時區 不受時區影響,純粹的日曆日期

使用時機

  • 只需要記錄「是哪一天」的情境,例如生日、節假日、會計期間等。
  • 不需要考慮時區或時間的精確度。

範例

-- 建立僅儲存日期的欄位
CREATE TABLE employee (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    birth_date DATE NOT NULL      -- 生日
);

-- 插入資料,MySQL 會自動檢查合法性
INSERT INTO employee (name, birth_date)
VALUES ('王小明', '1990-05-12');

-- 查詢 1990 年出生的員工
SELECT * FROM employee
WHERE birth_date BETWEEN '1990-01-01' AND '1990-12-31';

2. DATETIME

屬性 說明
儲存格式 YYYY‑MM‑DD HH:MM:SS(不含時區)
儲存空間 5 bytes(DATETIME(0))或 6~8 bytes(支援小數秒)
支援範圍 1000‑01‑01 00:00:00 ~ 9999‑12‑31 23:59:59
時區 與伺服器時區無關,儲存時不會自動轉換

使用時機

  • 必須同時保存「日期」與「時間」且不想受到時區影響的情況,例如歷史事件、預約時間(固定於特定時區)等。
  • 需要更高精度(可使用 DATETIME(3)DATETIME(6))時。

範例

-- 建立 DATETIME 欄位,預設值為目前時間
CREATE TABLE meeting (
    id INT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(100) NOT NULL,
    start_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    end_time   DATETIME NOT NULL
);

-- 插入會議資料,手動指定結束時間
INSERT INTO meeting (title, end_time)
VALUES ('產品發表會', '2025-12-01 15:30:00');

-- 取得今天之後的所有會議
SELECT *
FROM meeting
WHERE start_time >= CURDATE();

3. TIMESTAMP

屬性 說明
儲存格式 Unix epoch(自 1970‑01‑01 00:00:01 UTC 起的秒數)
儲存空間 4 bytes
支援範圍 1970‑01‑01 00:00:01 UTC ~ 2038‑01‑19 03:14:07 UTC
時區 受伺服器與連線時區影響,寫入時會自動轉換為 UTC,讀取時再依當前時區轉回本地時間

使用時機

  • 需要自動追蹤建立或更新時間DEFAULT CURRENT_TIMESTAMPON UPDATE CURRENT_TIMESTAMP)時最常使用。
  • 系統間同步資料、日誌紀錄、或需要以 UTC 為基準的分散式應用。

範例

-- 建立自動紀錄建立與更新時間的表
CREATE TABLE audit_log (
    id INT PRIMARY KEY AUTO_INCREMENT,
    action VARCHAR(200) NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- 新增一筆紀錄
INSERT INTO audit_log (action) VALUES ('使用者登入');

-- 模擬更新(updated_at 會自動更新)
UPDATE audit_log SET action = '使用者登出' WHERE id = 1;

-- 查詢並顯示 UTC 與本地時間差異
SELECT id,
       action,
       created_at,
       CONVERT_TZ(created_at, @@session.time_zone, '+00:00') AS created_utc
FROM audit_log;

4. 小結:何時選哪個?

需求 建議型別
只需要日期(不含時間) DATE
需要日期+時間且 不受時區 影響 DATETIME
需要自動追蹤、跨時區同步或以 UTC 為基準 TIMESTAMP

常見陷阱與最佳實踐

陷阱 說明 解決方式
時區混亂 TIMESTAMP 會自動依伺服器時區轉換,若開發、測試、正式環境時區不同,會造成時間錯位。 統一時區:在 my.cnf 設定 default_time_zone = '+00:00',或在連線時使用 SET time_zone = '+08:00';
超出範圍 TIMESTAMP 只能到 2038 年,若儲存未來遠期的日期(例如合約到 2050 年),會溢位。 使用 DATETIME 取代 TIMESTAMP
預設值不一致 DATETIME 在 MySQL 5.6 以前不支援 DEFAULT CURRENT_TIMESTAMP,會導致欄位預設為 NULL 確認 MySQL 版本,或改用 TIMESTAMP 作為自動時間戳記欄位。
毫秒/微秒需求 DATEDATETIMETIMESTAMP 預設不支援小數秒。 使用 DATETIME(3)DATETIME(6)TIMESTAMP(6)(MySQL 5.6+)來儲存毫秒/微秒。
字串與日期比較 直接比較 '2025-01-01'DATETIME 可能觸發隱式轉型,降低效能。 使用適當的類型,或在比較前使用 CAST(... AS DATETIME),並確保欄位上有索引。

最佳實踐

  1. 統一時區:開發、測試、上線皆使用 UTC,前端再根據使用者時區顯示。
  2. 使用 TIMESTAMP 作為 audit 欄位:自動 ON UPDATE CURRENT_TIMESTAMP 減少程式碼。
  3. 為日期欄位加索引:查詢範圍(BETWEEN)時效能大幅提升。
  4. 避免在 DATETIME 上使用 DEFAULT CURRENT_TIMESTAMP(除非 MySQL 5.6+),以免兼容性問題。
  5. 明確命名:如 created_atupdated_atevent_date,讓欄位意圖一目了然。

實際應用場景

1. 電子商務 – 訂單流程

欄位 型別 為何選此型別
order_date DATETIME 訂單產生的本地時間,需要精確到秒,且不因時區變化而改變(平台固定於台北時間)。
payment_timestamp TIMESTAMP 付款系統使用 UTC 記錄,便於跨國金融機構對帳。
delivery_deadline DATE 僅需顯示「送達日期」,不必關心時間。

2. 內容管理系統(CMS) – 文章發佈

  • published_at 使用 TIMESTAMP:允許排程發佈(未來時間),同時系統會自動轉為 UTC,避免因伺服器搬遷造成時間錯亂。
  • last_edited 使用 DATETIME:編輯者希望看到本地時間的編輯紀錄。
  • archive_date 使用 DATE:僅標示文章何時歸檔。

3. IoT 平台 – 感測資料

感測器上傳的資料包含毫秒級時間戳記,適合使用 TIMESTAMP(6),儲存 UTC,便於跨時區聚合與分析。


總結

  • DATE:僅記錄日期、儲存空間最小、與時區無關,適合生日、節假日等純日曆資訊。
  • DATETIME:記錄完整的「日期+時間」且不受時區影響,適合固定時區的業務流程或需要高精度的時間點。
  • TIMESTAMP:以 UTC 儲存、會自動依時區轉換,適合自動追蹤、跨時區同步或需要以 epoch 為基礎的系統。

在設計資料表時,先明確需求(是否需要時區、是否需要自動更新、時間範圍)再挑選最適合的型別,才能避免日後的資料錯誤與效能瓶頸。記得統一時區、加索引、善用 MySQL 提供的預設值與自動更新功能,讓你的 MySQL 應用更穩定、維護更輕鬆。祝你在 MySQL 的日期時間世界裡玩得開心!