Web 開發中,前端傳遞字符串日期,後端需解析為 Date 存入數據庫;查詢時再格式化為字符串返回前端。SimpleDateFormat 是 JDK 7 及之前的標準工具,但存在線程不安全、模式匹配嚴格等坑點,本文系統總結。
DateFormat:抽象父類,定義 format(Date)/parse(String)。SimpleDateFormat:具體實現,支持自定義模式。FastDateFormat (Apache Commons Lang):線程安全、不可變,推薦替代。| 字母 | 含義 | 示例 |
|---|---|---|
| y | 年 | yyyy=2024, yy=24 |
| M | 月 | MM=01, M=1 |
| d | 日 | dd=01, d=1 |
| H | 時 (0-23) | HH=13, H=13 |
| h | 時 (1-12) | hh=01, h=1 |
| m | 分 | mm=05, m=5 |
| s | 秒 | ss=09, s=9 |
| S | 毫秒 | SSS=123 |
| E | 星期 | EEE=Mon, EEEE=Monday |
| z | 時區 | GMT+08:00 |
| Z | RFC 822 時區 | +0800 |
| X | ISO 8601 時區 | +08:00, +0800, Z |
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(sdf.format(date)); // 2024-01-15
sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(date)); // 2024-01-15 14:30:45
sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
System.out.println(sdf.format(date)); // 2024年01月15日 14:30:45
String str = "2024-01-15 14:30:45";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(str); // 拋 ParseException,需 try-catch
| 輸入字符串 | 模式 | 結果 | 說明 |
|---|---|---|---|
| 2024-01-15 14:30:45 | yyyy-MM-dd HH:mm:ss | ✅ 成功 | 完全匹配 |
| 2024-01-15 | yyyy-MM-dd HH:mm:ss | ❌ ParseException | 字符串缺少時分秒,模式要求完整 |
| 2024-01-15 14:30:45 | yyyy-MM-dd | ✅ 成功 (時分秒為 0) | 模式比字符串短,多餘部分忽略 |
| 2024/01/15 | yyyy-MM-dd | ❌ ParseException | 分隔符不匹配 |
規則:解析時,字符串必須包含模式要求的所有字段;模式可比字符串短 (忽略尾部),不能比字符串長 (缺字段報錯)。
SimpleDateFormat 非線程安全:內部 Calendar 實例共享,併發調用 format/parse 會導致數據錯亂、拋 ArrayIndexOutOfBoundsException 或結果錯誤。
| 方案 | 代碼 | 優點 | 缺點 |
|---|---|---|---|
| 方法局部變量 | new SimpleDateFormat(...) | 簡單、絕對安全 | 頻繁創建對象,GC 壓力 |
| ThreadLocal 緩存 | ThreadLocal.withInitial(() -> new SimpleDateFormat(...)) | 復用實例、線程隔離 | 代碼較繁瑣、內存洩漏風險 (線程池) |
| FastDateFormat (推薦) | FastDateFormat.getInstance("yyyy-MM-dd") | 線程安全、不可變、高性能、單例 | 需引入 commons-lang3 |
| DateTimeFormatter (JDK 8+) | DateTimeFormatter.ofPattern(...) | 標準庫、線程安全、不可變、功能豐富 | 需遷移至 java.time 類型 |
// 判空比較:推薦模式
if (str == null || str.trim().isEmpty()) return null;
try {
return sdf.parse(str);
} catch (ParseException e) {
log.warn("日期解析失敗: {}", str, e);
return null; // 或拋業務異常
}
FastDateFormat (JDK 7) 或 DateTimeFormatter (JDK 8+),禁用 SimpleDateFormat 實例共享。DATE_PATTERN = "yyyy-MM-dd"。TIMESTAMP WITH TIME ZONE,展示層按用戶時區格式化。