杰瑞科技汇

Java.sql.Date 时间与 java.util.Date 有啥区别?

一句话总结

java.sql.Date 是一个过时且设计有缺陷的类,它只表示日期(年、月、日),不包含时间部分(时、分、秒、毫秒),它被设计用于与 JDBC 交互,将 java.util.Date 转换为 SQL DATE 类型。在现代 Java 开发中,应优先使用 java.time 包中的 java.time.LocalDate

Java.sql.Date 时间与 java.util.Date 有啥区别?-图1
(图片来源网络,侵删)

java.sql.Date 的本质与设计初衷

java.sql.Date 继承自 java.util.Date

public class java.sql.Date extends java.util.Date {
    // ... 实现
}

它的主要设计目的是为了与 JDBC 规范兼容,在数据库中,DATE 类型通常只存储日期信息(如 2025-10-27),而不存储时间。

当你从数据库中查询一个 DATE 类型的列时,JDBC 驱动程序会将其映射为 java.sql.Date 对象,同样,当你想将一个日期存入数据库的 DATE 列时,也需要使用 java.sql.Date

重要提示: 尽管它继承自 java.util.Date,但它不应该被当作一个完整的日期时间对象来使用,它的父类 java.util.Date 中的时间部分(时、分、秒、毫秒)被刻意设置为无效

Java.sql.Date 时间与 java.util.Date 有啥区别?-图2
(图片来源网络,侵删)

关键方法与陷阱

1 创建 java.sql.Date 对象

通常不使用 new java.sql.Date() 构造函数,而是使用静态工厂方法 valueOf(),因为它能更安全地解析字符串。

// 1. 从字符串创建 (推荐格式: yyyy-MM-dd)
String dateString = "2025-10-27";
java.sql.Date sqlDate = java.sql.Date.valueOf(dateString);
System.out.println("从字符串创建: " + sqlDate); // 输出: 2025-10-27
// 2. 从毫秒值创建 (注意:这会丢失时间信息)
java.util.Date utilDate = new java.util.Date(); // 当前完整时间
long millis = utilDate.getTime();
java.sql.Date sqlDateFromMillis = new java.sql.Date(millis);
System.out.println("从毫秒值创建: " + sqlDateFromMillis); // 输出只有日期部分,如 2025-10-27

2 获取日期信息

你可以使用继承自 java.util.Date 的方法来获取年、月、日。

java.sql.Date sqlDate = java.sql.Date.valueOf("2025-10-27");
// 注意:这些方法已经过时,但仍然可用
int year = sqlDate.getYear() + 1900; // getYear() 返回的是自1900年以来的年数,需要+1900
int month = sqlDate.getMonth() + 1;  // getMonth() 返回的是0-11,需要+1
int day = sqlDate.getDate();         // getDate() 返回的是1-31的日
System.out.println("年: " + year); // 输出: 年: 2025
System.out.println("月: " + month); // 输出: 月: 10
System.out.println("日: " + day);   // 输出: 日: 27

3 最大的陷阱:时间部分是无效的

这是 java.sql.Date 最令人困惑的地方。

java.sql.Date sqlDate = java.sql.Date.valueOf("2025-10-27");
// 尝试获取时间部分
int hours = sqlDate.getHours();      // 总是返回 0
int minutes = sqlDate.getMinutes();  // 总是返回 0
int seconds = sqlDate.getSeconds();  // 总是返回 0
System.out.println("小时: " + hours);   // 输出: 小时: 0
System.out.println("分钟: " + minutes); // 输出: 分钟: 0
System.out.println("秒: " + seconds);   // 输出: 秒: 0

java.sql.Date 的所有与时间相关的方法都返回0或无效值,它本质上是一个“伪”java.util.Date

Java.sql.Date 时间与 java.util.Date 有啥区别?-图3
(图片来源网络,侵删)

java.sql.Datejava.util.Date 的转换

1 java.sql.Date -> java.util.Date

由于是继承关系,可以直接赋值。

java.sql.Date sqlDate = java.sql.Date.valueOf("2025-10-27");
java.util.Date utilDate = sqlDate; // 向上转型,完全合法
System.out.println("转换后的 utilDate: " + utilDate);
// 输出: 转换后的 utilDate: 2025-10-27 00:00:00.0
// 注意:时间部分被清零了!

2 java.util.Date -> java.sql.Date

需要构造一个新的 java.sql.Date 对象,这会丢失原始 java.util.Date 中的所有时间信息

java.util.Date utilDate = new java.util Date(); // 假设是 2025-10-27 15:30:45.123
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
System.out.println("原始 utilDate: " + utilDate);
// 输出: 原始 utilDate: Fri Oct 27 15:30:45 CST 2025
System.out.println("转换后的 sqlDate: " + sqlDate);
// 输出: 转换后的 sqlDate: 2025-10-27
// 时间信息 15:30:45.123 完全丢失!

为什么不推荐使用 java.sql.Date

  1. 设计缺陷:它继承自 java.util.Date,但又“阉割”了其时间功能,这违反了面向对象的设计原则(is-a 关系),极易造成混淆。
  2. API 过时java.sql.Date 中的大部分方法(如 getYear(), getMonth())都已被标记为 @Deprecated,官方不推荐使用。
  3. 线程不安全java.util.Date 及其子类都不是线程安全的。
  4. 功能不完整:它无法表示时间,也无法处理时区等复杂场景。

现代替代方案:java.time 包 (Java 8+)

从 Java 8 开始,Java 引入了全新的 java.time 包,它提供了更强大、更安全、更易用的日期时间 API,这是处理日期时间的标准方式

1 对应 java.sql.Date 的类:java.time.LocalDate

LocalDate 表示一个不带时区的日期,完美地替代了 java.sql.Date 的核心功能。

2 在 JDBC 中使用 LocalDate

现代 JDBC (4.2+) 已经原生支持 java.time 类型,无需手动转换。

从数据库读取 DATE 列:

// 假设 rs 是你的 ResultSet 对象
LocalDate localDate = rs.getObject("birth_date", LocalDate.class);
// 或者
// LocalDate localDate = rs.getDate("birth_date").toLocalDate();

向数据库写入 DATE 列:

// 假设 ps 是你的 PreparedStatement 对象
LocalDate birthDate = LocalDate.of(1990, 5, 15);
ps.setObject("birth_date", birthDate);
// 或者
// ps.setDate("birth_date", java.sql.Date.valueOf(birthDate));

3 java.time 的其他重要类

描述 替代 java.sql 的什么?
LocalDate 仅日期,无时间 java.sql.Date
LocalTime 仅时间,无日期 -
LocalDateTime 日期 + 时间,无时区 java.sql.Timestamp
ZonedDateTime 日期 + 时间 + 时区 -
Instant 时间线上的一个瞬时点,与 UTC 时区相关 java.sql.Timestamp

总结与最佳实践

特性 java.sql.Date java.time.LocalDate (推荐)
用途 JDBC 交互,映射 SQL DATE 类型 表示一个日期(年、月、日)
时间部分 无效,总是 0 不包含时间部分
API 过时,不安全 现代,清晰,线程安全
可变性 可变 不可变 (线程安全)
JDBC 支持 原生支持 (旧版) 原生支持 (JDBC 4.2+)
创建方式 valueOf(String) of(int, int, int)parse(CharSequence)

最佳实践建议:

  1. 在业务逻辑层永远不要使用 java.sql.Date,统一使用 java.time 包中的类(如 LocalDate, LocalDateTime)。
  2. 在数据访问层:让 JDBC 驱动程序为你处理转换,直接使用 rs.getObject()ps.setObject() 来读写 LocalDateLocalDateTime
  3. 如果必须处理遗留代码:当你从数据库获取数据后,立即将其转换为 LocalDatejava.sql.Date sqlDate = rs.getDate(...); LocalDate localDate = sqlDate.toLocalDate();,当你需要存入数据库时,再进行转换:java.sql.Date sqlDate = java.sql.Date.valueOf(localDate);

遵循这个原则,你的代码将更加健壮、清晰且易于维护。

分享:
扫描分享到社交APP
上一篇
下一篇