Java 早期 java.util.Date + java.text.DateFormat 組合雖被 Java 8 java.time 取代,但在維護舊系統、Android 開發、快速腳本場景仍廣泛使用。本文系統梳理 DateFormat 四大預設風格、自定義模式、線程安全陷阱及現代替代方案。
import java.util.*;
import java.text.*;
public class DateStyleDemo {
public static void main(String[] args) {
Date now = new Date();
DateFormat dfDefault = DateFormat.getDateInstance(); // DEFAULT
DateFormat dfShort = DateFormat.getDateInstance(DateFormat.SHORT);
DateFormat dfMedium = DateFormat.getDateInstance(DateFormat.MEDIUM);
DateFormat dfLong = DateFormat.getDateInstance(DateFormat.LONG);
DateFormat dfFull = DateFormat.getDateInstance(DateFormat.FULL);
System.out.println("(DEFAULT) " + dfDefault.format(now));
System.out.println("(SHORT) " + dfShort.format(now));
System.out.println("(MEDIUM) " + dfMedium.format(now));
System.out.println("(LONG) " + dfLong.format(now));
System.out.println("(FULL) " + dfFull.format(now));
}
}
| 風格 | 輸出示例 | 適用場景 |
|---|---|---|
| SHORT | 12/31/23 | 緊湊顯示、表格列、日誌時間戳 |
| MEDIUM | Dec 31, 2023 | 通用業務顯示、報表、郵件 |
| LONG | December 31, 2023 | 正式文檔、合同、證書 |
| FULL | Sunday, December 31, 2023 | 用戶通知、行程單、完整語境 |
關鍵點:輸出深度依賴 Locale。中文環境下:SHORT=23-12-31、MEDIUM=2023年12月31日、LONG=2023年12月31日、FULL=2023年12月31日 星期日。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
System.out.println(sdf.format(now)); // 2023-12-31 23:59:59.123 CST
| 字母 | 含義 | 示例 |
|---|---|---|
| y | 年 | 2023, 23 |
| M | 月 | 12, Dec, December |
| d | 月中的日 | 31 |
| H | 小時 (0-23) | 23 |
| h | 小時 (1-12) | 11 |
| m | 分鐘 | 59 |
| s | 秒 | 59 |
| S | 毫秒 | 123 |
| E | 星期幾 | Sun, Sunday |
| z | 時區名 | CST, GMT+08:00 |
| Z | 時區偏移 | +0800 |
重複字母控制長度:MM=02、MMM=Dec、MMMM=December。
// 錯誤示範:靜態共享實例
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd");
// 多線程併發調用 SDF.format() 會導致數據錯亂、甚至拋 ArrayIndexOutOfBoundsException
// ThreadLocal 方案
private static final ThreadLocal TL_SDF = ThreadLocal.withInitial(
() -> new SimpleDateFormat("yyyy-MM-dd")
);
public static String format(Date date) {
return TL_SDF.get().format(date);
}
import java.time.*;
import java.time.format.*;
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(now.format(fmt)); // 線程安全、不可變、功能更強
| 舊 API | 新 API (java.time) | 優勢 |
|---|---|---|
| Date | Instant / LocalDateTime / ZonedDateTime | 不可變、清晰語義、納秒精度 |
| Calendar | ZonedDateTime / OffsetDateTime | 時區處理正確、流式 API |
| DateFormat | DateTimeFormatter | 線程安全、預編譯、解析更嚴格 |
| SimpleDateFormat | DateTimeFormatter.ofPattern() | 同左 |
SimpleDateFormat 必須用 ThreadLocal 或局部變量ISO-8601 ( yyyy-MM-dd'T'HH:mm:ss.SSSXXX )LocalDateTime、OffsetDateTimeZonedDateTime,避免 Date 隱含默認時區陷阱告別 SimpleDateFormat 線程安全噩夢,擁抱 java.time 的清晰與安全。