java及mysql日期问题
概述
首先,先概述一下存在的日期类型
java中的日期类型
java.util.Date(JDK 1.0)
基本功能:表示特定的时间点(毫秒精度),包含日期和时间信息。
问题:
- 设计不直观(年份从1900开始,月份从0开始)。
- 非线程安全(可变对象)。
- 时区处理复杂。
从 JDK 1.1 开始,应使用 Calendar 类实现日期和时间字段之间转换,使用 DateFormat 类来格式化和解析日期字符串。Date 中的相应方法已废弃
示例
Date date = new Date(); // 当前时间
java.util.Calendar(JDK 1.1)
改进:提供了对日期和时间的更多操作(如加减、格式化)。
- 用于日期计算和格式化(替代 Date 的部分功能)。用于日期计算和格式化(替代 Date 的部分功能)。
Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。
java.util.Date 是个日期数据;java.util.Calendar 用于日期相关的计算
问题:
- 依然存在设计冗余(如API 复杂、月份仍从 0 开始、性能较差。)。
- 可变对象,非线程安全。
示例
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);Calendar calendar = Calendar.getInstance();
calendar.set(2024, Calendar.MAY, 23); // 月份从 0 开始(5月是 4)
int year = calendar.get(Calendar.YEAR); // 2024
java.text.SimpleDateFormat(格式化)
功能:用于日期与字符串的相互转换。
问题:
- 非线程安全。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = sdf.format(new Date()); // 2025-04-21
Date parsedDate = sdf.parse("2025-04-21");
java.sql.Date/Time/Timestamp
java.sql.Date:与数据库的 DATE 类型对应(如MySQL的DATE)。
java.sql.Time:与数据库的 TIME 类型对应(如MySQL的TIME)。
java.sql.Timestamp:与数据库的 TIMESTAMP 或 DATETIME 类型对应(如MySQL的TIMESTAMP)。
与 java.util.Date 的转换
java.util.Date → java.sql.Date/Time/Timestamp:
java.util.Date utilDate = new java.util.Date();// 转换为 sql.Date(丢失时间部分)
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());// 转换为 sql.Time(丢失日期部分)
java.sql.Time sqlTime = new java.sql.Time(utilDate.getTime());// 转换为 sql.Timestamp(保留纳秒)
java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(utilDate.getTime());
java.sql.Date/Time/Timestamp → java.util.Date:
java.util.Date date = sqlDate; // 直接赋值(多态)
与 java.time 的交互(Java 8+)
从JDBC 4.2
开始,支持直接使用 java.time 类(如 LocalDate, LocalDateTime)与数据库交互
:
// 从数据库读取到 java.time 类
LocalDate localDate = resultSet.getObject("date_column", LocalDate.class);
LocalDateTime localDateTime = resultSet.getObject("timestamp_column", LocalDateTime.class);// 将 java.time 类写入数据库
preparedStatement.setObject(1, LocalDate.now());
preparedStatement.setObject(2, LocalDateTime.now());
java.sql → java.time
// java.sql.Date → LocalDate
LocalDate localDate = sqlDate.toLocalDate();
// java.sql.Timestamp → LocalDateTime
LocalDateTime localDateTime = timestamp.toLocalDateTime();
java.time→ java.sql
// LocalDate → java.sql.Date
java.sql.Date sqlDate = java.sql.Date.valueOf(localDate);// LocalDateTime → java.sql.Timestamp
java.sql.Timestamp timestamp = java.sql.Timestamp.valueOf(localDateTime);
注意事项
时间部分处理:这种设计容易导致逻辑混淆。
- java.sql.Date 会强制将时间部分(时、分、秒、毫秒)置零。
- java.sql.Time 会强制将日期部分置为1970-01-01。
时区问题:
- 所有 java.sql 类均不包含时区信息,依赖数据库或JVM默认时区。
- 若需时区支持,应使用 java.time.ZonedDateTime。
精度问题:
- java.sql.Timestamp 支持纳秒级精度,但某些数据库可能不兼容(如MySQL的DATETIME最多支持微秒)。
总结及区分
优先使用 java.time:在Java 8+中,应优先使用 java.time.LocalDate、LocalTime、LocalDateTime。
兼容旧代码:若需与遗留代码或旧JDBC驱动交互,使用 java.sql.Date/Time/Timestamp。
避免混用:不要将 java.util.Date 和 java.sql.Date 混用,它们的语义不同。
类 | 父类 | 用途 | 时间精度 | 时区处理 |
---|---|---|---|---|
java.util.Date | 无 | 通用日期时间(含日期和时间) | 毫秒 | 无时区(依赖JVM默认) |
java.sql.Date | java.util.Date | 无时间部分,仅表示日期(对应数据库的DATE类型) | 毫秒 | 无时区信息 |
java.sql.Time | java.util.Date | 无日期部分,仅时间(时:分:秒) | 无日期部分 | 无时区信息 |
java.sql.Timestamp | java.util.Date | 日期时间(含纳秒) | 纳秒级精度 | 继承 java.util.Date 时区 |
LocalDateTime | 无(java.time独立设计) | 表示本地日期和时间(无时区,如 2023-10-26T10:30:00) | 纳秒级 | 秒级 |
ZonedDateTime | 无(java.time独立设计) | 带时区的日期时间(如 2023-10-26T10:30:00+08:00[Asia/Shanghai]) | 纳秒级 | 秒级 |
mysql中的日期类型
五种类型及区别
类型 | 格式 | 存储空间 | 日期范围 | 时区处理 | 小数秒支持(精度) | 主要用途 | |
---|---|---|---|---|---|---|---|
DATE | YYYY-MM-DD | 3字节 | 1000-01-01 ~ 9999-12-31 | 无时区转换 | 不支持 | 仅需存储日期(如生日、签约日) | |
TIME | HH:MM:SS[.fraction] | 3字节+小数部分 | -838:59:59 ~ 838:59:59 | 无时区转换 | 存储时间或时间间隔(如任务耗时) | ||
DATETIME | YYYY-MM-DD HH:MM:SS[.fraction] | 5字节+小数部分 | 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 | 无时区转换(按写入值存储) | 支持(0~6位,默认0) | 存储精确时间(如订单创建时间) | |
TIMESTAMP | YYYY-MM-DD HH:MM:SS[.fraction] | 4字节+小数部分4字节+小数部分 | 1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07 UTC | 自动时区转换(存储为UTC,检索按当前时区显示) | 支持(0~6位,默认0) | 记录时间戳(如日志时间、最后修改时间) | |
YEAR | YYYY | 1字节 | 1901 ~ 2155 | 无时区转换 | 不支持 | 存储年份(如毕业年份) |
日期时间戳转换
UNIX时间戳转换为日期用函数: FROM_UNIXTIME()
select FROM_UNIXTIME(1156219870);
期转换为UNIX时间戳用函数: UNIX_TIMESTAMP()
Select UNIX_TIMESTAMP('2006-11-04 12:23:00');
二者类型映射建议
MySQL类型 | 格式示例 | 旧API(java.sql) | 新API(java.time) | 注意事项 |
---|---|---|---|---|
DATE | 2023-10-26 | java.sql.Date | java.time.LocalDate | 仅日期部分,时间被忽略;新旧API需通过valueOf()或toLocalDate()转换。 |
TIME | 14:30:45 或 -02:30:00 | java.sql.Time | java.time.LocalTime | 支持负数时间(如时间间隔);需注意数据库驱动对负数的支持。 |
DATETIME | 2023-10-26 14:30:45.123 | java.sql.Timestamp | java.time.LocalDateTime | 无时区信息,需确保应用与数据库时区一致;直接映射到LocalDateTime更直观。 |
TIMESTAMP | 2023-10-26 14:30:45 UTC | java.sql.Timestamp | java.time.Instant 或 ZonedDateTime | 存储为UTC,检索时自动转换时区;建议用Instant处理跨时区时间。 |
TEAR | 2023 | java.lang.Short/Integer | java.time.Year | 通常直接映射为Integer(如Year.getValue());Year类可增强语义明确性 |