杰瑞科技汇

Java.sql.date为何丢失时分秒信息?

java.sql.Date 不包含 时间信息(时、分、秒、毫秒),它只表示一个日期(年、月、日)。

Java.sql.date为何丢失时分秒信息?-图1
(图片来源网络,侵删)

当你尝试将一个包含时间信息的 java.util.Datejava.time.LocalDate 转换为 java.sql.Date 时,所有的时间部分都会被丢弃


详细解释

java.sql.Date 的设计初衷

java.sql.Date 是 JDBC API 的一部分,它的设计目的是为了与 SQL 标准中的 DATE 类型进行映射。

  • SQL DATE 类型: 在 SQL 标准中,DATE 类型只存储日期,不存储时间,在数据库中 DATE '2025-10-27' 只代表 2025 年 10 月 27 日这一天。
  • JDBC 映射: 为了与数据库的 DATE 类型对应,java.sql.Date 被设计为只持有日期信息,它继承自 java.util.Date,但通过重写 toString() 等方法,使其只显示日期部分。

为什么时间信息会丢失?

java.sql.Date 的构造函数和 valueOf() 方法会明确地将时间部分归零。

示例代码:

Java.sql.date为何丢失时分秒信息?-图2
(图片来源网络,侵删)
import java.sql.Date;
import java.util.Date as UtilDate; // 为了避免冲突,给 java.util.Date 起个别名
public class SqlDateExample {
    public static void main(String[] args) {
        // 1. 创建一个包含完整时间信息的 java.util.Date
        // 假设当前时间是 2025年10月27日 15:30:45.123
        UtilDate utilDate = new UtilDate();
        System.out.println("原始 java.util.Date: " + utilDate);
        // 输出类似: Fri Oct 27 15:30:45 CST 2025
        // 2. 将 java.util.Date 转换为 java.sql.Date
        // 关键点:这里会丢失时间信息!
        sqlDate sqlDate = new java.sql.Date(utilDate.getTime());
        System.out.println("转换后的 java.sql.Date: " + sqlDate);
        // 输出类似: 2025-10-27
        // 注意:时间部分 15:30:45.123 完全不见了
        // 3. 使用 valueOf 方法 (参数是 java.sql.Date 的内部表示格式)
        java.sql.Date sqlDateFromValueOf = java.sql.Date.valueOf("2025-10-27");
        System.out.println("通过 valueOf 创建的 java.sql.Date: " + sqlDateFromValueOf);
        // 输出: 2025-10-27
    }
}

从上面的例子可以清晰地看到,java.sql.Date 对象只保留了日期部分。


如何正确处理带时间的日期?

既然 java.sql.Date 不能存储时间,那么当你的业务需要存储日期和时间时,应该使用什么类型呢?

使用 java.sql.Timestamp (JDBC 时代)

java.sql.Timestamp 是 JDBC 提供的专门用于与 SQL TIMESTAMP 类型(或 DATETIME 类型)映射的类,它可以精确到纳秒级别。

使用场景: 当你需要将一个包含时间信息的 java.util.Date 存入数据库的 TIMESTAMPDATETIME 字段时,应该使用 java.sql.Timestamp

Java.sql.date为何丢失时分秒信息?-图3
(图片来源网络,侵删)

示例代码:

import java.sql.Timestamp;
import java.util.Date as UtilDate;
public class SqlTimestampExample {
    public static void main(String[] args) {
        // 1. 创建一个包含完整时间信息的 java.util.Date
        UtilDate utilDate = new UtilDate();
        System.out.println("原始 java.util.Date: " + utilDate);
        // 2. 将 java.util.Date 转换为 java.sql.Timestamp
        // Timestamp 会完整保留时间信息
        Timestamp timestamp = new Timestamp(utilDate.getTime());
        System.out.println("转换后的 java.sql.Timestamp: " + timestamp);
        // 输出类似: 2025-10-27 15:30:45.123
        // 3. 从 java.sql.Timestamp 转换回 java.util.Date
        UtilDate dateFromTimestamp = new Date(timestamp.getTime());
        System.out.println("从 Timestamp 转换回 java.util.Date: " + dateFromTimestamp);
        // 输出类似: Fri Oct 27 15:30:45 CST 2025
    }
}

JDBC 操作示例:

// 假设 conn 是一个有效的数据库连接
String sql = "INSERT INTO events (event_name, event_time) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
    pstmt.setString(1, "系统启动");
    pstmt.setTimestamp(2, new Timestamp(new java.util.Date().getTime())); // 使用 setTimestamp
    pstmt.executeUpdate();
} catch (SQLException e) {
    e.printStackTrace();
}

使用 java.time 包 (现代 Java 8+ 推荐)

自 Java 8 引入 java.time 包后,它已经成为处理日期和时间的标准、推荐方式,它比旧的 java.util.Datejava.sql.* 类更安全、更易用。

  • LocalDate: 对应 SQL DATE,只表示日期(年、月、日)。
  • LocalDateTime: 对应 SQL TIMESTAMPDATETIME,表示日期和时间,但不带时区信息。
  • ZonedDateTime: 带有时区信息的日期时间。

使用场景: 在业务逻辑层,强烈建议使用 java.time 中的类,在与数据库交互时,JDBC 4.2+ 规范可以直接使用这些 java.time 类型,JDBC 驱动会自动进行转换。

示例代码:

import java.sql.*;
import java.time.*;
public class JavaTimeExample {
    public static void main(String[] args) {
        // 1. 使用 java.time.LocalDateTime 表示一个带时间的日期
        LocalDateTime now = LocalDateTime.now();
        System.out.println("当前的 LocalDateTime: " + now);
        // 输出类似: 2025-10-27T15:30:45.123
        // 2. JDBC 4.2+ 中可以直接使用 LocalDateTime
        // 假设 conn 是一个有效的数据库连接
        String sql = "INSERT INTO events (event_name, event_time) VALUES (?, ?)";
        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, "定时任务执行");
            // 直接使用 setObject 传入 LocalDateTime
            pstmt.setObject(2, now); 
            pstmt.executeUpdate();
            System.out.println("数据插入成功!");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        // 3. 从数据库读取时,也可以直接获取为 LocalDateTime
        String querySql = "SELECT event_time FROM events WHERE event_name = ?";
        try (PreparedStatement pstmt = conn.prepareStatement(querySql)) {
            pstmt.setString(1, "定时任务执行");
            ResultSet rs = pstmt.executeQuery();
            if (rs.next()) {
                // 直接使用 getObject 获取为 LocalDateTime
                LocalDateTime eventTime = rs.getObject("event_time", LocalDateTime.class);
                System.out.println("从数据库读取到的时间: " + eventTime);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

总结与最佳实践

类型 对应 SQL 类型 包含信息 推荐使用场景 备注
java.sql.Date DATE 仅日期 (年-月-日) 不推荐,仅在与旧版 JDBC 或遗留代码交互时使用。 会丢失时间信息
java.sql.Timestamp TIMESTAMP / DATETIME 日期 + 时间 (纳秒级) 旧版项目 (Java 8 之前),当必须使用 JDBC 类型时,用它来处理时间。 java.util.Date 的子类,但行为特殊。
java.time.LocalDate DATE 仅日期 (年-月-日) 推荐,现代 Java (8+) 业务逻辑中处理日期。 首选,类型安全,不可变。
java.time.LocalDateTime TIMESTAMP / DATETIME 日期 + 时间 (纳秒级) 强烈推荐,现代 Java (8+) 业务逻辑中处理日期和时间。 首选,类型安全,不可变,JDBC 4.2+ 支持直接使用。

最终建议:

  1. 对于新项目:完全放弃 java.sql.Datejava.sql.Timestamp,在业务代码中统一使用 java.time 包下的 LocalDateLocalDateTime
  2. 对于数据库交互:如果你的 JDBC 驱动版本是 4.2 或更高,直接使用 pstmt.setObject()rs.getObject() 来读写 LocalDateLocalDateTime,这是最优雅的方式。
  3. 对于旧项目维护:如果必须使用 java.sql.Date,请务必记住它没有时间,并且在转换时主动处理时间信息的丢失或保留问题,如果需要时间,请使用 java.sql.Timestamp
分享:
扫描分享到社交APP
上一篇
下一篇