JDBC 規範中,java.sql.Date 僅保留「年月日」,java.sql.Time 僅保留「時分秒」,java.sql.Timestamp 則保留「納秒級」精度。當資料庫列類型為 DATETIME/TIMESTAMP 且包含完整時間信息時,若分別用 getDate()、getTime() 讀取,會發生靜默截斷:
2002-05-22 17:00:57.123getDate() → 2002-05-22(時分秒毫秒全丟)getTime() → 17:00:57(日期毫秒全丟)getTimestamp() → 完整保留DATETIME 精度達微秒(DATETIME(6)),TIMESTAMP 同理。DATE 精度至秒,TIMESTAMP 可達納秒;但 java.sql.Date 映射時仍會截斷。DATETIME 精度約 3.33ms,DATETIME2 可達 100ns。跨資料庫遷移或多數據源場景下,精度不一致極易導致邏輯錯誤。
Timestamp ts1 = new Timestamp(System.currentTimeMillis()); // 含納秒
Timestamp ts2 = new Timestamp(ts1.getTime()); // 納秒歸零
// 看似相等,實則不同
System.out.println(ts1.equals(ts2)); // false!因納秒不同
// 更隱蔽:Date vs Timestamp
Date d = new Date(ts1.getTime());
System.out.println(d.equals(ts1)); // true (Date 忽略納秒)
System.out.println(ts1.equals(d)); // false (Timestamp 比較納秒)
破壞了 equals 的對稱性,導致集合(Set、Map key)行為異常。
// 方案 1:合併毫秒與納秒
long fullTime = timestamp.getTime() + timestamp.getNanos() / 1_000_000;
// 方案 2:直接用 Instant (Java 8+)
Instant instant = timestamp.toInstant(); // 納秒精度
long epochMilli = instant.toEpochMilli(); // 毫秒
int nanoAdjustment = instant.getNano(); // 納秒餘數
java.time (Java 8+):LocalDateTime、Instant、ZonedDateTime 不可變、線程安全、精度納秒、API 清晰。ResultSet.getObject(col, LocalDateTime.class)、PreparedStatement.setObject(idx, localDateTime),避免 java.sql.* 類。Timestamp 承載完整時間,絕不混用 Date/Time 比較或存入集合。TIMESTAMP(6) 或 DATETIME(6),明確精度需求。java.sql.Date/Time 為 JDBC 1.0 早期設計,精度缺失是硬傷。現代 Java 應全面遷移至 java.time,從源頭消除精度陷阱,保證時間數據的準確性與一致性。