Java 日期时间的格式化主要涉及两个核心类:

java.text.SimpleDateFormat: 传统的、功能强大的格式化类,但存在线程不安全的问题。java.time.format.DateTimeFormatter: Java 8 引入的新日期时间 API 的一部分,是线程安全的,也是目前推荐使用的方式。
下面我们分别详细介绍这两个类,并提供最常用的格式化模式。
传统方式:SimpleDateFormat (不推荐在新代码中使用)
SimpleDateFormat 是一个非常灵活的类,可以通过模式字符串来定义日期时间的格式。
核心方法
format(Date date): 将Date对象格式化为字符串。parse(String source): 将字符串解析为Date对象。
格式化模式
模式字符是定义格式的关键,以下是一些最常用的模式字符:
| 字母 | 含义 | 示例 |
|---|---|---|
y |
年 | yyyy (2025), yy (23) |
M |
月 | MM (05), M (5) |
d |
月中的天 | dd (09), d (9) |
H |
小时 (24小时制) | HH (14), H (14) |
h |
小时 (12小时制) | hh (02), h (2) |
m |
分钟 | mm (05), m (5) |
s |
秒 | ss (09), s (9) |
S |
毫秒 | SSS (009) |
E |
星期几 | EEE (Tue), EEEE (Tuesday) |
a |
上午/下午标记 | AM, PM |
z |
时区 | Asia/Shanghai, GMT+08:00 |
代码示例
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatExample {
public static void main(String[] args) {
// 1. 创建一个格式化器
// 格式: 年-月-日 时:分:秒 星期几
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEEE");
// 2. 获取当前时间
Date now = new Date();
// 3. 格式化日期为字符串
String formattedDate = sdf.format(now);
System.out.println("格式化后的日期: " + formattedDate);
// 输出可能类似于: 格式化后的日期: 2025-10-27 15:30:45 Friday
// 4. 解析字符串为日期对象
String dateStr = "2025-12-25 10:00:00";
try {
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date parsedDate = sdf2.parse(dateStr);
System.out.println("解析后的日期对象: " + parsedDate);
// 输出类似于: 解析后的日期对象: Mon Dec 25 10:00:00 CST 2025
} catch (Exception e) {
e.printStackTrace();
}
}
}
⚠️ 重要警告:线程不安全
SimpleDateFormat 不是线程安全的,如果在多线程环境下共享同一个 SimpleDateFormat 实例,可能会导致数据错乱或程序异常。

错误示范 (多线程环境下):
// 不要这样做!
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
executor.submit(() -> {
try {
System.out.println(sdf.format(new Date()));
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
});
}
解决方案 (如果必须使用):
- 每次创建新实例: 在方法内部
new SimpleDateFormat(),性能开销大。 - 使用
ThreadLocal: 为每个线程创建一个独立的实例,这是常见的解决方案。
private static final ThreadLocal<SimpleDateFormat> threadLocalSDF =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
// 使用时
String dateStr = threadLocalSDF.get().format(new Date());
现代方式:DateTimeFormatter (推荐)
Java 8 引入了全新的 java.time 包,解决了旧 API 的所有痛点,DateTimeFormatter 就是其中用于格式化的类,它是线程安全的。
核心类关系
LocalDate: 表示一个日期 (年-月-日),如2025-10-27。LocalTime: 表示一个时间 (时-分-秒-纳秒),如15:30:45。LocalDateTime: 表示一个日期和时间 (年-月-日-时-分-秒),如2025-10-27T15:30:45。DateTimeFormatter: 负责格式化和解析上述对象。
格式化模式
DateTimeFormatter 的模式字符与 SimpleDateFormat 基本兼容,但更规范。

| 字母 | 含义 | 示例 |
|---|---|---|
y |
年 | yyyy (2025), yy (23) |
M |
月 | MM (05), M (5) |
d |
月中的天 | dd (09), d (9) |
H |
小时 (24小时制) | HH (14), H (14) |
h |
小时 (12小时制) | hh (02), h (2) |
m |
分钟 | mm (05), m (5) |
s |
秒 | ss (09), s (9) |
S |
纳秒 | SSSSSSSSS (000000009) |
n |
纳秒 (更常用) | n (9), nnnnnnnnn (000000009) |
E |
星期几 | EEE (Tue), EEEE (Tuesday) |
a |
上午/下午标记 | AM, PM |
Z |
时区偏移 | Z (UTC), GMT+8 |
V |
时区ID | Asia/Shanghai |
代码示例
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateTimeFormatterExample {
public static void main(String[] args) {
// 1. 创建一个格式化器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EEEE");
// 2. 获取当前日期时间
LocalDateTime now = LocalDateTime.now();
// 3. 格式化日期时间为字符串
String formattedDateTime = now.format(formatter);
System.out.println("格式化后的日期时间: " + formattedDateTime);
// 输出可能类似于: 格式化后的日期时间: 2025-10-27 15:30:45 Friday
// 4. 解析字符串为日期时间对象
String dateTimeStr = "2025-12-25 10:00:00";
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeStr, formatter2);
System.out.println("解析后的日期时间对象: " + parsedDateTime);
// 输出: 解析后的日期时间对象: 2025-12-25T10:00:00
// 5. 使用预定义的格式化器
DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
System.out.println("ISO格式: " + now.format(isoFormatter)); // 输出: 2025-10-27T15:30:45.123
}
}
常用格式化模式速查表
| 格式字符串 | 描述 | 示例 |
|---|---|---|
yyyy-MM-dd |
标准日期格式 | 2025-10-27 |
yyyyMMdd |
紧凑日期格式 | 20251027 |
dd/MM/yyyy |
欧洲风格日期 | 27/10/2025 |
MM/dd/yyyy |
美国风格日期 | 10/27/2025 |
HH:mm:ss |
24小时制时间 | 15:30:45 |
hh:mm:ss a |
12小时制时间 | 03:30:45 PM |
yyyy-MM-dd HH:mm:ss |
完整日期时间 (常用) | 2025-10-27 15:30:45 |
yyyy-MM-dd'T'HH:mm:ss |
ISO 8601 标准格式 | 2025-10-27T15:30:45 |
yyyy-MM-dd'T'HH:mm:ss.SSS |
带毫秒的ISO 8601格式 | 2025-10-27T15:30:45.123 |
yyyy-MM-dd'T'HH:mm:ss.SSSZ |
带时区偏移的ISO 8601格式 | 2025-10-27T15:30:45.123+0800 |
EEE, d MMM yyyy |
RFC 1123 格式 (如邮件头) | Fri, 27 Oct 2025 |
yyyyMMddHHmmss |
紧凑日期时间 (日志文件名常用) | 20251027153045 |
总结与最佳实践
| 特性 | SimpleDateFormat (旧) |
DateTimeFormatter (新) |
|---|---|---|
| 线程安全 | ❌ 不安全 | ✅ 安全 |
| API 设计 | 基于 java.util.Date,设计有缺陷 |
基于 java.time,类型更明确 (LocalDate, LocalTime 等) |
| 时区处理 | 复杂,容易出错 | 清晰,有专门的 ZonedDateTime, OffsetDateTime |
| 闰秒等 | 无法处理 | 正确处理 |
| 推荐度 | 仅用于维护旧代码 | 强烈推荐用于所有新项目 |
核心建议:
-
新项目必须使用
java.timeAPI,即LocalDateTime,ZonedDateTime配合DateTimeFormatter。 -
如果必须维护旧的
java.util.Date代码,可以通过Date.from(localDateTime.toInstant())和Date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()进行转换。 -
将
DateTimeFormatter定义为static final常量,因为它可以被安全地共享,提高性能。public class DateFormatter { private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy年MM月dd日"); public static String format(LocalDate date) { return date.format(DATE_FORMATTER); } }
