日期時間處理是 Java 開發高頻痛點:格式不匹配、時區陷阱、java.util.Date 與 java.sql.Timestamp 繼承關係導致的精度遺失。本文系統整理三者互轉代碼模板,並針對常見坑點給出規避方案。
String dateStr = "2024/06/15 14:30:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
try {
Date date = sdf.parse(dateStr); // 拋 ParseException 需捕獲
System.out.println(date); // Sat Jun 15 14:30:00 CST 2024
} catch (ParseException e) {
e.printStackTrace();
}
⚠️ 線程安全警示:SimpleDateFormat 非線程安全,多線程環境請用 ThreadLocal 或 Java 8+ DateTimeFormatter。
Date date = new Date();
// 可任意自訂格式
DateFormat sdf1 = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
DateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH/mm/ss");
System.out.println(sdf1.format(date)); // 2024/06/15 14:30:00
System.out.println(sdf2.format(date)); // 2024-06-15 14/30/00
String tsStr = "2024-06-15 14:30:00.123"; // 必須符合 yyyy-MM-dd HH:mm:ss[.f...]
Timestamp ts = Timestamp.valueOf(tsStr); // 靜態工廠方法
System.out.println(ts); // 2024-06-15 14:30:00.123
格式嚴格:不符合標準格式會拋 IllegalArgumentException,非標準字串請先用 SimpleDateFormat 解析為 Date 再轉 Timestamp。
Timestamp ts = new Timestamp(System.currentTimeMillis());
// 方式一:自訂格式 (推薦)
String custom = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(ts);
// 方式二:預設 toString() 輸出 yyyy-MM-dd HH:mm:ss.fffffffff
String defaultStr = ts.toString();
Timestamp extends Date,故 向上轉型無損,向下轉型需強轉。
Timestamp ts = new Timestamp(System.currentTimeMillis());
Date date = ts; // 自動向上轉型
// 注意:date 實際仍指向 Timestamp 物件,getTime() 保留納秒精度
Date date = new Date();
// 方式一:經由 long 毫秒值 (推薦,無精度損失)
Timestamp ts1 = new Timestamp(date.getTime());
// 方式二:若 date 實際是 Timestamp 實例,可強轉
if (date instanceof Timestamp) {
Timestamp ts2 = (Timestamp) date;
}
| 場景 | 推薦做法 | 避坑說明 |
|---|---|---|
| 寫入完整日期時間 | ps.setTimestamp(idx, new Timestamp(new Date().getTime())) | setDate 僅存日期,會丟失時分秒 |
| 讀取完整日期時間 | rs.getTimestamp(idx) → new Date(ts.getTime()) | 直接 rs.getDate() 會截斷時間部分 |
| ORM 映射 | 實體類用 java.time.LocalDateTime (Java 8+) | 避免舊 API 時區/精度問題 |
// 解析
LocalDateTime ldt = LocalDateTime.parse("2024-06-15T14:30:00", DateTimeFormatter.ISO_LOCAL_DATE_TIME);
// 格式化
String str = ldt.format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"));
// 與舊 API 互通
Date date = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
Timestamp ts = Timestamp.valueOf(ldt);
強烈建議:新專案全面遷移至 java.time.*,線程安全、不可變、API 清晰,徹底解決舊 API 歷史包袱。
| 轉換方向 | 核心 API | 關鍵注意 |
|---|---|---|
| String → Date | SimpleDateFormat.parse() | 格式必須完全匹配,線程安全用 ThreadLocal |
| Date → String | SimpleDateFormat.format() | 格式任意自訂 |
| String → Timestamp | Timestamp.valueOf() | 僅接受標準格式 yyyy-MM-dd HH:mm:ss[.f...] |
| Timestamp → String | ts.toString() 或 SimpleDateFormat.format(ts) | 前者固定格式含納秒,後者可自訂 |
| Date → Timestamp | new Timestamp(date.getTime()) | 保留毫秒精度,納秒歸零 |
| Timestamp → Date | 直接賦值 Date d = ts; | 向上轉型,實際物件不變 |