MySQL 時間類型與 Java JDBC 類型的正確對應,是避免資料截斷、時區偏移、精度遺失的關鍵。本文系統整理類型對應表、儲存範圍差異、插入查詢實戰代碼與最佳實踐。
類型對應總覽表
| MySQL 類型 | Java 類型 (java.sql) | 儲存範圍 | 精度/特性 |
DATE | java.sql.Date | 1000-01-01 ~ 9999-12-31 | 僅日期,無時間元件 |
DATETIME | java.sql.Timestamp | 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 | 日期+時間,無時區資訊 |
TIMESTAMP | java.sql.Timestamp | 1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07 UTC | 自動轉換時區,受 2038 年問題影響 |
TIME | java.sql.Time | -838:59:59 ~ 838:59:59 | 僅時間,可表示持續時間 |
YEAR | java.sql.Date | 1901 ~ 2155 | 僅年份,MySQL 8.0 已棄用 YEAR(2) |
關鍵差異深度解析
DATE:純日期儲存
- 僅保留年月日,插入含時間的值會自動截斷時間部分
- 支援字串格式
'YYYY-MM-DD' 與數字格式 YYYYMMDD 插入
- Java 端用
ResultSet.getDate() 取得 java.sql.Date
DATETIME vs TIMESTAMP:核心差異
| 特性 | DATETIME | TIMESTAMP |
| 儲存空間 | 8 bytes (MySQL 5.6.4+) | 4 bytes |
| 時區感知 | 否,原樣儲存 | 是,寫入轉 UTC,讀出轉會話時區 |
| 範圍 | 年份 1000-9999 | 1970-2038 (32-bit 限制) |
| 預設值 | 無 | 可設 CURRENT_TIMESTAMP 自動更新 |
| 適用場景 | 生日、歷史事件、排程時間 | 建立/更新時間戳、跨時區應用 |
實戰測試代碼 (JDBC)
public void testDateTypes() throws SQLException {
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Taipei",
"root", "password");
try (Statement stmt = conn.createStatement()) {
// 建立測試表
stmt.execute("DROP TABLE IF EXISTS time_test");
stmt.execute("CREATE TABLE time_test (" +
"id INT AUTO_INCREMENT PRIMARY KEY, " +
"dt_date DATE, " +
"dt_datetime DATETIME, " +
"dt_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, " +
"dt_time TIME, " +
"dt_year YEAR" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
// 插入測試資料
String sql = "INSERT INTO time_test (dt_date, dt_datetime, dt_time, dt_year) VALUES " +
"('2024-01-15', '2024-01-15 14:30:45', '14:30:45', 2024), " +
"(20240115, 20240115143045, 143045, 2024)";
stmt.executeUpdate(sql);
// 查詢驗證
ResultSet rs = stmt.executeQuery("SELECT * FROM time_test");
while (rs.next()) {
System.out.println("DATE: " + rs.getDate("dt_date"));
System.out.println("DATETIME: " + rs.getTimestamp("dt_datetime"));
System.out.println("TIMESTAMP: " + rs.getTimestamp("dt_timestamp"));
System.out.println("TIME: " + rs.getTime("dt_time"));
System.out.println("YEAR: " + rs.getDate("dt_year")); // YEAR 映射為 Date
}
}
}
常見坑點與解決方案
- 時區不一致導致 TIMESTAMP 偏移:JDBC URL 加入
serverTimezone=Asia/Taipei (或 UTC)
- DATE 插入數字格式補零問題:
2024115 會被解析為 2024-01-15,建議統一用字串格式
- YEAR 類型在 MySQL 8.0+ 行為變更:
YEAR(2) 已移除,統一為 4 位數
- java.util.Date vs java.sql.Date:JDBC 專用
java.sql.* 類別,避免用 java.util.Date 直接存取
- 2038 年問題:長期專案避免用 TIMESTAMP,改用 DATETIME 或 BIGINT 存 Unix 毫秒數
最佳實踐建議
- 應用層統一使用
java.time.LocalDateTime (Java 8+),轉換為 java.sql.Timestamp 寫入
- 資料庫連線池設定
connectionInitSql=SET time_zone='+08:00' 固定會話時區
- 建表時明確指定
DEFAULT CHARSET=utf8mb4 避免字符集問題
- 重要時間欄位加上索引,但避免在 WHERE 條件對時間欄位做函數運算