1. 概述

在本篇文章中,我们将深入探讨 Java 8 引入的 DateTimeFormatter 类及其格式化模式。我们还会结合实际使用场景,展示该类的多种用法。

DateTimeFormatter 提供了统一的方式来格式化日期和时间,支持预定义格式或自定义格式,非常适合在应用中保持日期时间展示的一致性。

2. 使用预定义实例的 DateTimeFormatter

DateTimeFormatter 内置了多个符合 ISO 和 RFC 标准的预定义格式。例如,我们可以使用 ISO_LOCAL_DATE 来格式化日期,如 “2018-03-09”:

DateTimeFormatter.ISO_LOCAL_DATE.format(LocalDate.of(2018, 3, 9));

如果需要包含时区偏移量,可以使用 ISO_OFFSET_DATE,输出如 “2018-03-09-03:00”:

DateTimeFormatter.ISO_OFFSET_DATE.format(LocalDate.of(2018, 3, 9).atStartOfDay(ZoneId.of("UTC-3")));

⚠️ 大部分预定义格式都遵循 ISO-8601 标准,这是国际通用的日期时间表示方式。

不过,还有一个特殊的预定义格式是 RFC_1123_DATE_TIME,它遵循 RFC-1123 标准(IETF 发布):

DateTimeFormatter.RFC_1123_DATE_TIME.format(LocalDate.of(2018, 3, 9).atStartOfDay(ZoneId.of("UTC-3")));

输出结果为:

Fri, 9 Mar 2018 00:00:00 -0300

有时候我们接收到的是字符串形式的日期,且格式已知,这时就可以使用 parse() 方法进行解析:

LocalDate.from(DateTimeFormatter.ISO_LOCAL_DATE.parse("2018-03-09")).plusDays(3);

这段代码的结果是 2018 年 3 月 12 日的 LocalDate 实例。

3. 使用 FormatStyleDateTimeFormatter

在需要更人性化的日期展示时,我们可以使用 FormatStyle 枚举(FULL, LONG, MEDIUM, SHORT)配合 DateTimeFormatter

LocalDate anotherSummerDay = LocalDate.of(2016, 8, 23);
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).format(anotherSummerDay));
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).format(anotherSummerDay));
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(anotherSummerDay));
System.out.println(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).format(anotherSummerDay));

输出结果为:

Tuesday, August 23, 2016
August 23, 2016
Aug 23, 2016
8/23/16

对于日期+时间的格式,需要使用 ZonedDateTime 实例,并调用 ofLocalizedDateTime() 方法:

LocalDate anotherSummerDay = LocalDate.of(2016, 8, 23);
LocalTime anotherTime = LocalTime.of(13, 12, 45);
ZonedDateTime zonedDateTime = ZonedDateTime.of(anotherSummerDay, anotherTime, ZoneId.of("Europe/Helsinki"));
System.out.println(
  DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)
  .format(zonedDateTime));

输出为:

Tuesday, August 23, 2016 1:12:45 PM EEST

我们也可以反向解析:

ZonedDateTime dateTime = ZonedDateTime.from(
  DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)
    .parse("Tuesday, August 23, 2016 1:12:45 PM EET"));
System.out.println(dateTime.plusHours(9));

输出结果为:

2016-08-23T22:12:45+03:00[Europe/Bucharest]

4. 自定义格式的 DateTimeFormatter

虽然预定义格式能满足大多数场景,但有时我们需要更灵活的自定义格式。

4.1. 日期格式

例如,我们想用欧洲常用的格式 dd.MM.yyyy 展示日期:

String europeanDatePattern = "dd.MM.yyyy";
DateTimeFormatter europeanDateFormatter = DateTimeFormatter.ofPattern(europeanDatePattern);
System.out.println(europeanDateFormatter.format(LocalDate.of(2016, 7, 31)));

输出为:

31.07.2016

常用日期格式符号如下:

符号 含义 示例
u 年份 2004; 04
y 年代 2004; 04
M/L 月份 7; 07; Jul; July
d 日期 10

⚠️ 注意:格式符号的数量会影响输出。例如,MM 会补零,而 M 则不会。

4.2. 时间格式

时间格式符号如下:

符号 含义 示例
H 小时(0-23) 0
m 分钟 30
s 55
S 毫秒 978
n 纳秒 987654321

示例:

String timeColonPattern = "HH:mm:ss";
DateTimeFormatter timeColonFormatter = DateTimeFormatter.ofPattern(timeColonPattern);
LocalTime colonTime = LocalTime.of(17, 35, 50);
System.out.println(timeColonFormatter.format(colonTime));

输出为:

17:35:50

若要显示 AM/PM,使用 hh:mm:ss a

String timeColonPattern = "hh:mm:ss a";
DateTimeFormatter timeColonFormatter = DateTimeFormatter.ofPattern(timeColonPattern);
LocalTime colonTime = LocalTime.of(17, 35, 50);
System.out.println(timeColonFormatter.format(colonTime));

输出为:

05:35:50 PM

4.3. 时区格式

如果需要显示时区,可以使用 z

String newYorkDateTimePattern = "dd.MM.yyyy HH:mm z";
DateTimeFormatter newYorkDateFormatter = DateTimeFormatter.ofPattern(newYorkDateTimePattern);
LocalDateTime summerDay = LocalDateTime.of(2016, 7, 31, 14, 15);
System.out.println(newYorkDateFormatter.format(ZonedDateTime.of(summerDay, ZoneId.of("UTC-4"))));

输出为:

31.07.2016 14:15 UTC-04:00

解析时也需注意格式一致性,否则会抛出 DateTimeParseException

4.4. 使用 Locale 的格式化

我们可以结合 Locale 实现本地化格式:

LocalDate date = LocalDate.of(2023, 9, 18);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd, yy: EEE").withLocale(Locale.US);
String formattedDate = date.format(formatter);

输出为:

Sep 18, 23: Mon

切换为韩语 Locale:

formatter = DateTimeFormatter.ofPattern("MMM dd, yy: EEE").withLocale(Locale.KOREA);

输出为:

9월 18, 23: 월

4.5. Instant 格式化

DateTimeFormatter.ISO_INSTANT 专门用于格式化 UTC 时间戳:

ZonedDateTime zonedDateTime = ZonedDateTime.of(2021, 02, 15, 0, 0, 0, 0, ZoneId.of("Europe/Paris"));
String formattedZonedDateTime = DateTimeFormatter.ISO_INSTANT.format(zonedDateTime);

输出为:

2021-02-14T23:00:00Z

⚠️ 注意:解析时必须指定时区,否则会抛出异常。

4.6. 本地化模式匹配(Java 19 新特性)

Java 19 引入了 ofLocalizedPattern() 方法,用于根据 Locale 匹配最合适的格式,而不是直接本地化。

⚠️ 该方法可能会抛出异常,如果找不到匹配的格式。

5. 总结

本文介绍了 DateTimeFormatter 的多种使用方式,包括预定义格式、自定义格式、本地化格式等,适用于大多数日期时间处理场景。在实际开发中,掌握这些技巧可以让你更轻松地应对各种格式化需求。✅


原始标题:Guide to DateTimeFormatter | Baeldung