首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

java.util.Date 與 java.sql.Date 差異深度解析:轉換最佳實踐與時區陷阱

     編輯:Java 相關知識

核心差異總覽

特性java.util.Datejava.sql.Date
套件java.utiljava.sql
精度毫秒 (日期 + 時間)天 (僅日期,時間分量被正規化為 00:00:00)
繼承Objectextends java.util.Date
用途通用日期時間處理JDBC DATE 類型映射、資料庫交互
構造器Date(), Date(long ms)Date(int year, int month, int day) @Deprecated, Date(long ms)
toString 格式dow mon dd hh:mm:ss zzz yyyyyyyy-MM-dd

為什麼會有 java.sql.Date?

JDBC 規範定義三種時間類型對應 SQL 標準:

  • java.sql.Date → SQL DATE (年月日)
  • java.sql.Time → SQL TIME (時分秒)
  • java.sql.Timestamp → SQL TIMESTAMP (年月日時分秒毫秒/奈秒)

java.sql.Date 繼承 java.util.Date 但強制將時分秒毫秒歸零,確保資料庫 DATE 欄位不存時間資訊。

互相轉換最佳實踐

1. util.Date → sql.Date (保留日期,捨棄時間)

java.util.Date utilDate = new java.util.Date(); // 當前日期時間
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime()); // 截取日期部分

// 注意:若 utilDate 為 2024-06-15 14:30:00.123
// sqlDate 將為 2024-06-15 (00:00:00.000 本地時區)

2. sql.Date → util.Date (直接賦值,多態)

java.sql.Date sqlDate = java.sql.Date.valueOf("2024-06-15");
java.util.Date utilDate = sqlDate; // 向上轉型,時間為 00:00:00

3. 字串 → sql.Date (標準格式 yyyy-MM-dd)

// 方式一:valueOf (最簡單,嚴格要求 yyyy-MM-dd)
java.sql.Date sqlDate = java.sql.Date.valueOf("2024-06-15");

// 方式二:LocalDate → sql.Date (Java 8+ 推薦)
java.time.LocalDate localDate = java.time.LocalDate.parse("2024-06-15");
java.sql.Date sqlDate = java.sql.Date.valueOf(localDate);

// 方式三:SimpleDateFormat → util.Date → sql.Date (舊版)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date utilDate = sdf.parse("2024-06-15");
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());

4. sql.Date → 字串 (格式化輸出)

// 方式一:toString() 直接輸出 yyyy-MM-dd
String str = sqlDate.toString(); // "2024-06-15"

// 方式二:LocalDate 格式化 (Java 8+)
String formatted = sqlDate.toLocalDate().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"));

// 方式三:SimpleDateFormat (舊版)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
String formatted = sdf.format(sqlDate);

時區陷阱:關鍵認知


// 同一個瞬間,不同時區的 "日期" 可能不同!
// 假設 UTC 時間 2024-06-15 02:00:00
// 時區 UTC+8 (北京) -> 日期 2024-06-15
// 時區 UTC-5 (紐約) -> 日期 2024-06-14

// java.sql.Date 建構子使用「系統預設時區」截取日期
java.util.Date moment = ...; // 某個 Instant
java.sql.Date sqlDate = new java.sql.Date(moment.getTime()); // 依賴 JVM 時區

// 跨時區應用:必須明確指定時區
// 錯誤:new java.sql.Date(moment.getTime())  // 系統時區
// 正確:使用 Java 8+ java.time
ZonedDateTime zdt = moment.toInstant().atZone(ZoneId.of("Asia/Shanghai"));
java.sql.Date correctDate = java.sql.Date.valueOf(zdt.toLocalDate());

Java 8+ java.time 完美替代方案

實務代碼範本

public class DateConversionUtils {
    /** util.Date -> sql.Date (系統時區) */
    public static java.sql.Date toSqlDate(java.util.Date date) {
        return date == null ? null : new java.sql.Date(date.getTime());
    }

    /** sql.Date -> util.Date */
    public static java.util.Date toUtilDate(java.sql.Date date) {
        return date; // 多態直接賦值
    }

    /** 字串(yyyy-MM-dd) -> sql.Date */
    public static java.sql.Date parseSqlDate(String str) {
        return str == null ? null : java.sql.Date.valueOf(str);
    }

    /** sql.Date -> 格式化字串 */
    public static String formatSqlDate(java.sql.Date date, String pattern) {
        if (date == null) return null;
        return date.toLocalDate().format(java.time.format.DateTimeFormatter.ofPattern(pattern));
    }

    /** 跨時區:Instant -> 指定時區的 sql.Date */
    public static java.sql.Date toSqlDateAtZone(java.time.Instant instant, java.time.ZoneId zone) {
        return instant == null ? null : java.sql.Date.valueOf(instant.atZone(zone).toLocalDate());
    }
}

總結

  • java.util.Date:日期+時間,通用,但可變、非執行緒安全、API 設計差。
  • java.sql.Date:僅日期 (歸零時間),JDBC DATE 映射,繼承 util.Date 易誤用。
  • 核心轉換:new java.sql.Date(utilDate.getTime()) 與 java.sql.Date.valueOf("yyyy-MM-dd")。
  • 時區陷阱:sql.Date 建構依賴 JVM 時區,跨時區應用必須用 java.time 明確指定時區。
  • 新專案:全面使用 java.time.LocalDate、Instant、ZonedDateTime,僅在 JDBC 邊界轉換 sql.Date。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
舊類型現代替代轉換橋樑
java.util.Datejava.time.Instant / ZonedDateTimeDate.toInstant(), Date.from(instant)
java.sql.Datejava.time.LocalDateDate.valueOf(localDate), sqlDate.toLocalDate()
java.sql.Timejava.time.LocalTimeTime.valueOf(localTime), sqlTime.toLocalTime()
java.sql.Timestampjava.time.LocalDateTime / InstantTimestamp.from(instant), ts.toInstant()