源本科技 | 码上会

Java 日期时间:现代 API

2026/01/30
40
0

学习目标

  • 掌握 java.time 包中 LocalDateTimeLocalDateZonedDateTime 等核心类的格式化方法

  • 熟练使用 DateTimeFormatter 进行自定义和预定义格式的处理

  • 理解新 API 在格式化方面的线程安全性与国际化支持优势

正文内容

为什么需要新格式化

Java 8 的 java.time 包不仅重构了日期时间模型,也彻底重写了格式化机制:

  • 使用 不可变、线程安全DateTimeFormatter

  • 支持 ISO-8601 标准 开箱即用

  • 提供 本地化(Locale)时区感知 的格式化能力

  • 避免了 SimpleDateFormat 的所有历史问题

使用预定义格式器

DateTimeFormatter 提供了多个静态常量,符合国际标准或常见习惯:

LocalDateTime now = LocalDateTime.now();

// ISO 标准格式(无时区)
System.out.println(now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
// 输出:2026-01-30T15:42:30.123

// 仅日期(ISO)
System.out.println(LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE));
// 输出:2026-01-30

// 带毫秒的时间
System.out.println(LocalTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME));

自定义格式模式

使用 ofPattern(String pattern) 创建自定义格式器:

LocalDateTime now = LocalDateTime.now();

DateTimeFormatter custom = DateTimeFormatter.ofPattern("yyyy 年 MM 月 dd 日 HH:mm:ss");
String formatted = now.format(custom);
System.out.println("自定义格式:" + formatted);
// 输出:2026 年 01 月 30 日 15:42:30

常用模式符号(与 SimpleDateFormat 类似但更规范):

符号

含义

示例

yyyy

四位年份

2026

MM

两位月份

01, 12

dd

两位日期

05, 30

HH

24 小时制

09, 23

mm

分钟

07, 59

ss

03, 45

SSS

毫秒

123

E

星期(如“周五”)

需配合 Locale

z

时区缩写

CST, GMT+8

V

时区 ID

Asia/Shanghai

注意:DateTimeFormatter 对大小写敏感,M 表示月份,m 表示分钟。

带本地化的格式化

可指定语言环境,自动适配不同地区的日期习惯:

LocalDateTime now = LocalDateTime.now();
DateTimeFormatter zhFormatter = DateTimeFormatter
    .ofPattern("yyyy 年 MM 月 dd 日 EEEE HH:mm", Locale.CHINA);

System.out.println(now.format(zhFormatter));
// 输出:2026 年 01 月 30 日 星期五 15:42

解析字符串为日期时间

与格式化对称,DateTimeFormatter 也支持安全解析:

String input = "2026 年 01 月 30 日 15:30:00";
DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy 年 MM 月 dd 日 HH:mm:ss");

LocalDateTime parsed = LocalDateTime.parse(input, parser);
System.out.println("解析结果:" + parsed);

优势:parse() 方法在格式不匹配时直接抛出 DateTimeParseException,错误明确;且不会自动修正无效日期(如 2 月 30 日会报错),行为更可预测。

线程安全与性能

DateTimeFormatter不可变对象,因此天然线程安全,可安全地作为静态常量复用:

public class DateUtils {
    public static final DateTimeFormatter CHINESE_FORMATTER =
        DateTimeFormatter.ofPattern("yyyy 年 MM 月 dd 日 HH:mm:ss");
    
    public static String format(LocalDateTime dt) {
        return dt.format(CHINESE_FORMATTER); // 安全!
    }
}

无需 ThreadLocal,也无需每次新建实例,性能和安全性兼得。

处理带时区的格式化

对于 ZonedDateTime,可包含时区信息:

ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
DateTimeFormatter withZone = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss VV");

System.out.println(zdt.format(withZone));
// 输出:2026-01-30 15:42:30 Asia/Shanghai

其中:

  • VV 表示完整的时区 ID(如 Asia/Shanghai

  • z 表示时区缩写(如 CST),但可能不唯一,建议慎用

重点总结

  • DateTimeFormatter 是 Java 8+ 推荐的日期格式化工具,线程安全、功能强大、行为可预测。

  • 支持预定义 ISO 格式、自定义模式、本地化(Locale)和时区输出。

  • 解析时默认严格校验,避免“静默错误”。

  • 所有操作基于不可变对象,符合函数式编程思想,适合现代应用开发。

思考题

  1. 为什么 DateTimeFormatter 能做到线程安全,而 SimpleDateFormat 不能?从设计层面分析。

  2. 如何用 DateTimeFormatter 解析一个包含中文星期几的字符串(如 “2026 年 01 月 30 日 星期五”)?

  3. 在 RESTful API 中返回日期时间字段时,应选择 LocalDateTime 还是 ZonedDateTime?对应的格式应该如何设计?