日期時間格式化是 Java 開發的高頻需求。SimpleDateFormat 作為最常用的格式化工具,功能強大但也存在著名的線程不安全問題。本文全面解析其用法、模式字母、常見陷阱及現代替代方案。
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFormatTest {
public static void main(String[] args) {
// 注意:String 不要寫成 Strings
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date currentTime = new Date();
String currentTimedf = df.format(currentTime);
System.out.println("現在時間是: " + currentTimedf);
}
}
| 字母 | 含義 | 示例 | 備註 |
|---|---|---|---|
| y | 年 | 2024 / 24 | yyyy=4位,yy=2位 |
| M | 月 | 07 / Jul / July | MM=兩位,MMM=縮寫,MMMM=全稱 |
| d | 月中的天 | 05 / 5 | |
| H | 一天中的小時 (0-23) | 14 | 24小時制 |
| h | 上午/下午中的小時 (1-12) | 02 | 12小時制 |
| m | 小時中的分鐘 | 30 | |
| s | 分鐘中的秒 | 45 | |
| S | 毫秒 | 123 | SSS=三位毫秒 |
| E | 星期幾 | 週二 / Tuesday | |
| a | AM/PM 標記 | PM | |
| z | 時區名稱 | CST / GMT+08:00 | |
| Z | 時區偏移量 | +0800 | RFC 822 格式 |
| X | ISO 8601 時區 | +08:00 / Z | Java 7+ |
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 2024-01-15 14:30:45
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); // 2024/01/15
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年M月d日 E HH:mm"); // 2024年1月15日 週一 14:30
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); // 2024-01-15T14:30:45+08:00 (ISO 8601)
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); // 20240115143045 (文件名時間戳)
SimpleDateFormat 是線程不安全的! 多線程共享同一實例會導致格式化結果錯誤、拋出異常甚至數據損壞。
錯誤示例:
// 危險!多線程環境下共享實例
public class UnsafeDateFormat {
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd");
public String format(Date date) {
return SDF.format(date); // 併發調用會出錯
}
}
正確解決方案:
// 方案 1:局部變量(推薦簡單場景)
public String format(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
}
// 方案 2:ThreadLocal(高併發場景)
public class SafeDateFormat {
private static final ThreadLocal SDF_THREAD_LOCAL =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
public String format(Date date) {
return SDF_THREAD_LOCAL.get().format(date);
}
}
// 方案 3:Java 8+ DateTimeFormatter(現代標準,線程安全)
import java.time.format.DateTimeFormatter;
import java.time.LocalDateTime;
public class ModernDateFormat {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public String format(LocalDateTime dateTime) {
return FORMATTER.format(dateTime); // 線程安全!
}
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date date = sdf.parse("2024-01-15 14:30:45");
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}
新項目強烈建議使用 java.time 包:
LocalDate、LocalTime、LocalDateTime - 不帶時區ZonedDateTime、OffsetDateTime - 帶時區DateTimeFormatter - 線程安全的格式化器Instant - 時間戳(替代 Date)