MySQL 时间类型与 Java 日期时间类对应关系详解
一、MySQL 时间类型与 Java 类型对应关系
MySQL 类型 | 范围/描述 | Java 对应类型 | 精度级别 |
---|---|---|---|
DATE | '1000-01-01' - '9999-12-31' | java.time.LocalDate |
日期 |
TIME | '-838:59:59' - '838:59:59' | java.time.LocalTime |
时间 |
DATETIME | '1000-01-01 00:00:00' - '9999-12-31 23:59:59' | java.time.LocalDateTime |
毫秒/微秒 |
TIMESTAMP | '1970-01-01 00:00:01' - '2038-01-19 03:14:07' | java.time.Instant java.sql.Timestamp |
秒/毫秒 |
YEAR | 1901 - 2155 | java.time.Year java.lang.Integer |
年 |
二、JDBC 操作映射关系
1. 从数据库读取数据
try (ResultSet rs = stmt.executeQuery("SELECT * FROM time_table")) {
while (rs.next()) {
LocalDate date = rs.getObject("date_column", LocalDate.class);
LocalTime time = rs.getObject("time_column", LocalTime.class);
LocalDateTime dateTime = rs.getObject("datetime_column", LocalDateTime.class);
Instant timestamp = rs.getObject("ts_column", Instant.class);
}
}
2. 向数据库写入数据
PreparedStatement ps = conn.prepareStatement(
"INSERT INTO time_table (date_column, time_column, datetime_column, ts_column) " +
"VALUES (?, ?, ?, ?)");
ps.setObject(1, LocalDate.now());
ps.setObject(2, LocalTime.now());
ps.setObject(3, LocalDateTime.now());
ps.setObject(4, Instant.now());
ps.executeUpdate();
三、MySQL 时间类型详细对比
特性 | DATE | TIME | DATETIME | TIMESTAMP | YEAR |
---|---|---|---|---|---|
时区敏感 | 否 | 否 | 否 | 是 | 否 |
自动初始化 | 否 | 否 | 否 | 是 | 否 |
自动更新 | 否 | 否 | 否 | 是 | 否 |
存储空间(字节) | 3 | 3 | 5-8 | 4 | 1 |
微秒精度支持 | - | MySQL 5.6+ | MySQL 5.6+ | MySQL 5.6.4+ | - |
2038年限制 | 无 | 无 | 无 | 有 (2038年) | 无 |
四、微秒精度处理(MySQL 5.6+)
// Java 端处理微秒(MySQL DATETIME(6))
LocalDateTime ldt = LocalDateTime.now()
.withNano(456000000); // 456毫秒
// MySQL 创建表
CREATE TABLE events (
event_time DATETIME(6) // 支持微秒级精度
);
五、时区处理最佳实践
1. 时间存储策略
// 写入统一使用 UTC 时间
ps.setObject(4, Instant.now()); // UTC 时间戳
// 读取时转换为本地时间
Instant dbTime = rs.getObject("event_time", Instant.class);
ZonedDateTime localTime = dbTime.atZone(ZoneId.systemDefault());
2. MySQL 服务器配置
-- 检查时区设置
SELECT @@global.time_zone, @@session.time_zone;
-- 推荐设置为 UTC
SET GLOBAL time_zone = '+00:00';
六、特殊场景处理方案
1. 日期范围统计
LocalDate start = LocalDate.of(2023, 1, 1);
LocalDate end = LocalDate.of(2023, 12, 31);
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM orders WHERE order_date BETWEEN ? AND ?");
ps.setObject(1, start);
ps.setObject(2, end);
2. 时间区间查询(TIMESTAMP + 时区)
Instant startInstant = ZonedDateTime.of(
2023, 5, 1, 0, 0, 0, 0,
ZoneId.of("Asia/Shanghai")).toInstant();
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM transactions WHERE create_time >= ?");
ps.setObject(1, startInstant);
3. 时间间隔计算(Java 端处理)
LocalDateTime orderTime = rs.getObject("order_time", LocalDateTime.class);
LocalDateTime deliveryTime = rs.getObject("delivery_time", LocalDateTime.class);
Duration interval = Duration.between(orderTime, deliveryTime);
System.out.printf("配送耗时: %.1f小时", interval.toMinutes() / 60.0);
七、传统 java.sql 类替代方案
传统类 | Java 8+ 替代方案 | 使用场景 |
---|---|---|
java.sql.Date |
LocalDate |
只处理日期部分 |
java.sql.Time |
LocalTime |
只处理时间部分 |
java.sql.Timestamp |
Instant 或 LocalDateTime |
带时区的时间戳/本地日期时间 |
转换示例:
// 传统转现代
Timestamp ts = rs.getTimestamp("create_time");
LocalDateTime ldt = ts.toLocalDateTime();
// 现代转传统
LocalDateTime now = LocalDateTime.now();
Timestamp ts = Timestamp.valueOf(now);
八、时区转换流程图
graph LR
A[业务系统时间\nLocalDateTime] -->|应用时区| B[JVM 默认时区]
B -->|转换为UTC| C[Instant/UTC时间]
C -->|存储至MySQL| D[TIMESTAMP列]
D -->|读取为Instant| E[Java Instant类型]
E -->|转换至目标时区| F[用户时区显示]
九、常见问题解决方案
-
时区错乱问题
// 明确设置JVM时区(启动参数) -Duser.timezone=Asia/Shanghai // JDBC连接指定时区 jdbc:mysql://localhost/db?serverTimezone=Asia/Shanghai
-
2038年TIMESTAMP溢出
- 在 MySQL 8.0 升级 TIMESTAMP 到 8 字节存储
- 将 TIMESTAMP 改为 DATETIME 类型
-
微秒精度丢失
// 数据库指定精度(DATETIME(6)) LocalDateTime ldt = LocalDateTime.now().withNano(123456000);
-
跨时区同步问题
- 统一使用 UTC 时间存储(TIMESTAMP)
- 前端显示时根据用户时区转换
总结建议
-
类型选择优先级:
- 日期 →
DATE
↔LocalDate
- 时间 →
TIME
↔LocalTime
- 完整时间 →
DATETIME
↔LocalDateTime
- 跨时区时间 →
TIMESTAMP
↔Instant
- 日期 →
-
最佳实践:
// 创建表规范 CREATE TABLE events ( id BIGINT PRIMARY KEY, event_date DATE, -- 日期类数据 event_time TIME(3), -- 毫秒级时间 start_datetime DATETIME(6), -- 微秒级本地时间 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 带时区时间 ); // Java操作规范 repository.save(new Event( LocalDate.now(), LocalTime.now().truncatedTo(ChronoUnit.MILLIS), LocalDateTime.now().withNano(456000000), Instant.now() ));
-
重要原则:
- 前端交互:字符串(ISO 8601格式)
- 业务层:java.time 类型
- 持久层:直接映射数据库类型
- 存储时态:统一使用UTC标准时区
-
调试技巧:
-- MySQL查看实际存储值 SELECT date_column, UNIX_TIMESTAMP(ts_column) AS timestamp_value, CONVERT_TZ(ts_column, '+00:00', @@session.time_zone) AS local_time FROM time_table;
掌握MySQL和Java时间类型的精确对应关系,是处理国际化应用、财务系统和时效敏感业务的基础。遵循UTC存储、本地化展示的原则,可规避90%的时区相关问题。