Skip to content

DateTimeFormatter

DateTimeFormatter 用于格式化和解析日期时间,是 SimpleDateFormat 的替代品。

最重要的改进:线程安全

为什么需要 DateTimeFormatter

SimpleDateFormat 的问题:

java
// ❌ SimpleDateFormat 不是线程安全的
public class DateUtils {
    private static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd");
    
    public static Date parse(String date) {
        return SDF.parse(date);  // ❌ 并发调用会出问题
    }
}

// ✅ DateTimeFormatter 是线程安全的
public class DateUtils {
    private static DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    
    public static LocalDate parse(String date) {
        return LocalDate.parse(date, FMT);  // ✅ 安全
    }
}

常用模式

日期格式

java
import java.time.*;
import java.time.format.*;

LocalDate date = LocalDate.of(2024, 3, 15);

DateTimeFormatter fmt1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
System.out.println(date.format(fmt1));  // "2024-03-15"

DateTimeFormatter fmt2 = DateTimeFormatter.ofPattern("yyyy/MM/dd");
System.out.println(date.format(fmt2));  // "2024/03/15"

DateTimeFormatter fmt3 = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
System.out.println(date.format(fmt3));  // "2024年03月15日"

DateTimeFormatter fmt4 = DateTimeFormatter.ofPattern("yyyyMMdd");
System.out.println(date.format(fmt4));  // "20240315"

时间格式

java
LocalTime time = LocalTime.of(14, 30, 45);

DateTimeFormatter fmt1 = DateTimeFormatter.ofPattern("HH:mm:ss");
System.out.println(time.format(fmt1));  // "14:30:45"

DateTimeFormatter fmt2 = DateTimeFormatter.ofPattern("HH:mm");
System.out.println(time.format(fmt2));  // "14:30"

DateTimeFormatter fmt3 = DateTimeFormatter.ofPattern("a HH:mm");
System.out.println(time.format(fmt3));  // "下午 14:30"

日期时间格式

java
LocalDateTime dt = LocalDateTime.of(2024, 3, 15, 14, 30, 45);

DateTimeFormatter fmt1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(dt.format(fmt1));  // "2024-03-15 14:30:45"

DateTimeFormatter fmt2 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
System.out.println(dt.format(fmt2));  // "2024-03-15T14:30:45"(ISO 格式)

预定义格式器

JDK 内置了很多常用格式:

java
// ISO 标准格式
LocalDate date = LocalDate.now();
LocalTime time = LocalTime.now();
LocalDateTime dt = LocalDateTime.now();

date.format(DateTimeFormatter.ISO_LOCAL_DATE);        // 2024-03-15
time.format(DateTimeFormatter.ISO_LOCAL_TIME);        // 14:30:45
dt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);    // 2024-03-15T14:30:45

// 紧凑格式
date.format(DateTimeFormatter.BASIC_ISO_DATE);       // 20240315

// 与 Locale 结合
DateTimeFormatter localized = DateTimeFormatter.ofPattern("yyyy年M月d日", Locale.CHINA);
date.format(localized);  // "2024年3月15日"

解析

java
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");

// 解析日期
LocalDate date = LocalDate.parse("2024-03-15", fmt);

// 解析时间
LocalTime time = LocalTime.parse("14:30:45", DateTimeFormatter.ofPattern("HH:mm:ss"));

// 解析日期时间
LocalDateTime dt = LocalDateTime.parse("2024-03-15 14:30:45",
    DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

处理解析异常

java
import java.time.format.*;

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");

try {
    LocalDate date = LocalDate.parse("2024-13-45", fmt);  // 无效日期
} catch (DateTimeParseException e) {
    System.err.println("解析失败: " + e.getMessage());
}

// 或使用 withResolverStyle 宽松解析
DateTimeFormatter lenient = DateTimeFormatter.ofPattern("yyyy-MM-dd")
    .withResolverStyle(ResolverStyle.LENIENT);
LocalDate lenientDate = LocalDate.parse("2024-13-45", lenient);  // 解析为 2025-02-14

带时区的格式化

java
import java.time.zone.*;

ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));

// 格式化带时区
DateTimeFormatter fmt1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z");
System.out.println(zdt.format(fmt1));  // "2024-03-15 14:30:45 +08:00"

// 使用预定义格式
DateTimeFormatter fmt2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss VV");
System.out.println(zdt.format(fmt2));  // "2024-03-15 14:30:45 Asia/Shanghai"

常见使用场景

场景一:API 响应格式化

java
@RestController
public class UserController {
    
    private static final DateTimeFormatter FMT = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        user.setFormattedDate(user.getCreateTime().format(FMT));
        return user;
    }
}

场景二:文件命名

java
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
String filename = "report_" + now.format(fmt) + ".pdf";
// "report_20240315_143045.pdf"

场景三:日志时间格式化

java
public class LogFormatter {
    
    private static final DateTimeFormatter FMT = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
    
    public String format(Instant timestamp) {
        return LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault())
            .format(FMT);
    }
}

模式字母速查

字母含义示例
y2024
M03 / March
d15
H小时(0-23)14
h小时(1-12)2
m分钟30
s45
S毫秒123
aAM/PM下午
E星期星期五 / Fri
Z时区偏移+08:00
VV时区 IDAsia/Shanghai

小结

方法说明
DateTimeFormatter.ofPattern()创建格式化器
localDate.format(formatter)格式化为字符串
LocalDate.parse(str, formatter)从字符串解析
DateTimeFormatter.ISO_LOCAL_DATE预定义 ISO 格式

记住:DateTimeFormatter 是线程安全的,可以在多线程环境中共享使用。

基于 VitePress 构建