日期處理是業務開發高頻場景,但 Date、Calendar、SimpleDateFormat、LocalDateTime 混用極易出 Bug。本文梳理核心類轉換鏈路、線程安全陷阱、Java 8 新 API 最佳實踐,並附一道高頻面試題實戰解析。
┌─────────────┐ getTime()/ctor ┌────────┐
│ java.util.Date ◄──────────────────► │ long │ (毫秒時間戳)
└──────┬──────┘ └────────┘
│ setTime()/getTime()
▼
┌──────────────────┐
│ java.util.Calendar │ (欄位運算、時區感知)
└────────┬─────────┘
│ getTime()
▼
┌──────────────────┐
│ java.util.Date │
└────────┬─────────┘
│ SimpleDateFormat.format/parse
▼
┌──────────────────┐
│ String │ (業務展示/存儲/傳輸)
└──────────────────┘
// Date → long Date date = new Date(); long ts = date.getTime(); // long → Date long now = System.currentTimeMillis(); Date date2 = new Date(now);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
// Date → String
String str = sdf.format(new Date());
// String → Date (拋 ParseException)
Date parsed = sdf.parse("2024-06-15 14:30:00.123");
⚠ 線程安全大坑:
SimpleDateFormat非線程安全,不可作為 static 字段共享。正確做法:
① 局部變量每次 new
② ThreadLocal 緩存
③ Java 8+ 用DateTimeFormatter(不可變、線程安全)
// Date → Calendar Calendar cal = Calendar.getInstance(); cal.setTime(date); // Calendar → Date Date date3 = cal.getTime(); // 日期運算範例:+3 天 / -1 月 cal.add(Calendar.DATE, 3); cal.add(Calendar.MONTH, -1);
| 類 | 用途 | 關鍵特性 |
|---|---|---|
| LocalDate | 純日期 (年月日) | 無時區、不可變 |
| LocalTime | 純時間 (時分秒) | 無時區、不可變 |
| LocalDateTime | 日期時間 | 無時區、不可變 |
| ZonedDateTime | 帶時區日期時間 | 時區感知、夏令時自動處理 |
| Instant | 時間戳 (納秒精度) | 機器時間、可與 Date 互轉 |
| DateTimeFormatter | 格式化/解析 | 線程安全、預定義格式豐富 |
// Date → Instant → LocalDateTime (系統默認時區)
LocalDateTime ldt = date.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
// LocalDateTime → Date
Date newDate = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
// 格式化
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str = ldt.format(fmt);
LocalDateTime parsed = LocalDateTime.parse(str, fmt);
/**
* 參數:Date date
* 返回:date 往後推 3 天,格式 "yyyy-MM-dd"
*/
public String add3Days(Date date) {
// 1. 校驗空指針
if (date == null) return null;
// 2. 推薦:Java 8 一行流
return date.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate()
.plusDays(3)
.format(DateTimeFormatter.ISO_LOCAL_DATE);
// 3. 兼容舊項目:Calendar 版
/*
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DATE, 3);
return new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime());
*/
}
SimpleDateFormat 不作 static 字段,改用 DateTimeFormatter 或 ThreadLocalZonedDateTime / Instant,禁用 DateTIMESTAMP / BIGINT(毫秒),應用層按用戶時區展示2024-06-15T14:30:00Z) 而非時間戳一言蔽之:新項目直接上
java.time全家桶;維舊項目邊緣封裝工具類隔離SimpleDateFormat,逐步替換。日期處理沒有銀彈,只有規範與測試。