java.util.date 和 java.sql.date 语义不同、无继承关系:前者表示精确到毫秒的时刻,后者仅保留年月日并强制归零时分秒;转换需显式处理时区与截断,推荐使用 java.time 的 localdate 和 instant 替代。

java.util.Date 和 java.sql.Date 虽然名字相似,但设计目的和行为差异明显,转换时不能简单强转,否则容易出错(比如时间部分被清零、时区异常、格式丢失等)。
核心区别:语义不同,不是父子关系
java.util.Date 表示精确到毫秒的时刻(instant),包含年月日时分秒毫秒;
java.sql.Date 是为 JDBC 设计的“仅日期”类型,**逻辑上只保留年-月-日,时分秒毫秒强制归零**(即使构造时带时间,toString 也只显示日期,且底层 getTime() 返回的毫秒值对应当天 00:00:00 UTC)。
二者没有继承关系,只是都实现了 Serializable 和 Cloneable。
从 java.util.Date → java.sql.Date:需显式截断时间
直接 new java.sql.Date(utilDate.getTime()) 是常见做法,但要注意:
- 它把 utilDate 的毫秒值原样传入,然后内部按 JDBC 规范解释为“该毫秒值对应的本地时区的 00:00:00”,再转成 UTC 时间戳存储;
- 实际效果是:取 utilDate 所在时区的“日期部分”,归零其时分秒后,转为对应 UTC 时间戳。
- 推荐写法(明确语义):
cal.setTime(utilDate);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
java.sql.Date sqlDate = new java.sql.Date(cal.getTimeInMillis());
这样确保以当前 JVM 时区为准,清除时间部分后再构造。
从 java.sql.Date → java.util.Date:可直接用 getTime() 构造
因为 sqlDate.getTime() 返回的是一个毫秒值(对应当天 00:00:00 UTC),所以:
- java.util.Date utilDate = new java.util.Date(sqlDate.getTime()); —— 安全且常用
- 注意:得到的 utilDate 在你本地时区会显示为“当天 00:00:00”,但它的毫秒值是 UTC 零点,跨时区使用时可能有显示偏差
现代替代方案:优先用 java.time
Java 8+ 强烈建议用 LocalDate 表示纯日期,Instant 或 ZonedDateTime 表示时刻:
- LocalDate date = sqlDate.toLocalDate(); // 无歧义
- java.sql.Date.valueOf(localDate); // 反向构造
- Instant instant = utilDate.toInstant(); // 更清晰地表达“时刻”概念
避免旧 Date 类的隐式时区陷阱,代码意图更明确,线程安全,API 更合理。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











