Java 日期格式化常見需求:將 Date 轉為「yyyy-MM-dd 上午/下午 hh:mm:ss」格式。本文提供最簡潔的 SimpleDateFormat 範例,並補充 **執行緒安全問題**、**Java 8 新 API 替代方案**與 **時區/語系陷阱**避坑指南。
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class DateFormatDemo {
public static void main(String[] args) {
// a 代表 AM/PM 標記,hh 為 12 小時制
String pattern = "yyyy-MM-dd a hh:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.getDefault()); // 明確指定 Locale
System.out.println(sdf.format(new Date()));
// 輸出範例:2024-05-20 下午 03:45:12
}
}
| 符號 | 含義 | 範例 |
|---|---|---|
| yyyy | 年(4 位) | 2024 |
| MM | 月(2 位) | 05 |
| dd | 日(2 位) | 20 |
| a | AM/PM 標記 | 上午/下午 |
| hh | 12 小時制(01-12) | 03 |
| HH | 24 小時制(00-23) | 15 |
| mm | 分 | 45 |
| ss | 秒 | 12 |
| E | 星期幾 | 週一 |
SimpleDateFormat 為**有狀態**類別,多執行緒共用同一實例會導致格式錯誤或拋出異常。
static final SimpleDateFormat SDF = new SimpleDateFormat(...); 共用。private static final ThreadLocal<SimpleDateFormat> TL_SDF = ThreadLocal.withInitial(() ->
new SimpleDateFormat("yyyy-MM-dd a hh:mm:ss", Locale.getDefault()));
public static String format(Date date) { return TL_SDF.get().format(date); }
new SimpleDateFormat(pattern).format(date)DateTimeFormatter。import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.ZoneId;
public class ModernDateFormat {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd a hh:mm:ss", Locale.getDefault());
public static String formatNow() {
return LocalDateTime.now(ZoneId.systemDefault()).format(FORMATTER);
}
public static String format(Date date) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().format(FORMATTER);
}
}
DateTimeFormatter **不可變、執行緒安全**、效能更好、API 更豐富(解析/格式化分離)。ZoneId 精確控制時區,避免預設時區導致的跨日錯誤。a 標記輸出語言取決於 Locale:
Locale.CHINA / Locale.TAIWAN → 「上午/下午」Locale.US → 「AM/PM」Locale**,避免伺服器預設語系導致輸出不一致。DateTimeFormatter**。SimpleDateFormat,採用 ThreadLocal 或每次新建。正確處理日期格式化,是避免生產環境離奇 Bug 的關鍵細節。