8.5 解析与格式化

问题

用户希望解析或格式化 Date-Time API 中的类。

方案

DateTimeFormatter 类用于创建日期 / 时间格式,可以在解析和格式化中使用。

讨论

DateTimeFormatter 类提供大量预定义格式化器(predefined formatter),包括常量(如 ISO_LOCAL_DATE)、模式字母(如 uuuu-MMM-dd)以及本地化样式(如 ofLocalizedDate(dateStyle))。

好在解析和格式化的过程并不复杂。在 Date-Time API 中,所有主要的类均提供 parseformat 方法。以 LocalDate 类为例,其 parseformat 方法的签名如例 8-28 所示。

例 8-28 LocalDate 类定义的 parseformat 方法

  1. static LocalDate parse(CharSequence text)
  2. static LocalDate parse(CharSequence text, DateTimeFormatter formatter)
  3. String format(DateTimeFormatter formatter)

➊ 使用 ISO_LOCAL_DATE

解析和格式化的应用如例 8-29 所示。

例 8-29 对 LocalDateTime 解析和格式化

  1. LocalDateTime now = LocalDateTime.now();
  2. String text = now.format(DateTimeFormatter.ISO_DATE_TIME);
  3. LocalDateTime dateTime = LocalDateTime.parse(text);

❶ 将 LocalDateTime 格式化为字符串

❷ 将字符串解析为 LocalDateTime

因此,我们可以调整日期 / 时间格式、区域设置等各种参数。部分应用如例 8-30 所示。

例 8-30 对日期进行格式化

  1. LocalDate date = LocalDate.of(2017, Month.MARCH, 13);
  2.  
  3. System.out.println("Full : " +
  4. date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)));
  5. System.out.println("Long : " +
  6. date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)));
  7. System.out.println("Medium : " +
  8. date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)));
  9. System.out.println("Short : " +
  10. date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)));
  11.  
  12. System.out.println("France : " +
  13. date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)
  14. .withLocale(Locale.FRANCE)));
  15. System.out.println("India : " +
  16. date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)
  17. .withLocale(new Locale("hin", "IN"))));
  18. System.out.println("Brazil : " +
  19. date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)
  20. .withLocale(new Locale("pt", "BR"))));
  21. System.out.println("Japan : " +
  22. date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)
  23. .withLocale(Locale.JAPAN)));
  24.  
  25. Locale loc = new Locale.Builder()
  26. .setLanguage("sr")
  27. .setScript("Latn")
  28. .setRegion("RS")
  29. .build();
  30. System.out.println("Serbian: " +
  31. date.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)
  32. .withLocale(loc)));

执行上述程序,输出类似于:10

10有传言说,作者有意选择稀有语种和输出格式,只是为了测试 O'Reilly Media 能否正确打印出相应的结果。不过至少就读者所知,这并非事实。

  1. Full : Monday, March 13, 2017
  2. Long : March 13, 2017
  3. Medium : Mar 13, 2017
  4. Short : 3/13/17
  5. France : lundi 13 mars 2017
  6. India : Monday, March 13, 2017
  7. Brazil : Segunda-feira, 13 de Março de 2017
  8. Japan : 2017 3 13
  9. Serbian: ponedeljak, 13. mart 2017

由于 parseformat 方法分别抛出 DateTimeParseExceptionDateTimeException,用户可能希望在自己的代码中捕获它们。

我们也可以通过 DateTimeFormatter 类提供的 ofPattern 方法创建自定义格式化器,Javadoc 详细描述了所有可以使用的合法值。ofPattern 方法的应用如例 8-31 所示。

例 8-31 自定义格式化模式

  1. ZonedDateTime moonLanding = ZonedDateTime.of(
  2. LocalDate.of(1969, Month.JULY, 20),
  3. LocalTime.of(20, 18),
  4. ZoneId.of("UTC")
  5. );
  6. System.out.println(moonLanding.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
  7. DateTimeFormatter formatter =
  8. DateTimeFormatter.ofPattern("uuuu/MMMM/dd hh:mm:ss a zzz GG");
  9. System.out.println(moonLanding.format(formatter));
  10. formatter = DateTimeFormatter.ofPattern("uuuu/MMMM/dd hh:mm:ss a VV xxxxx");
  11. System.out.println(moonLanding.format(formatter));

输出如下:

  1. 1969-07-20T20:18:00Z[UTC]
  2. 1969/July/20 08:18:00 PM UTC AD
  3. 1969/July/20 08:18:00 PM UTC +00:00

有关 DateTimeFormatter 类的用途以及各种模式字母的含义,请参见 Javadoc。不过读者无须担心,格式化的过程并不复杂。

接下来,我们通过夏时制(daylight savings time)问题来展示本地化日期 / 时间格式化器的应用。北美东部时区(EST)从 2018 年 3 月 11 日凌晨 2 时起实行夏时制,将时钟调快一小时。那么在 3 月 11 日凌晨 2 时 30 分时,某个地区的日期和时间是多少呢?相关示例如例 8-32 所示。

例 8-32 将时钟调快一小时

  1. ZonedDateTime zdt = ZonedDateTime.of(2018, 3, 11, 2, 30, 0, 0,
  2. ZoneId.of("America/New_York"));
  3. System.out.println(
  4. zdt.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)));

在本例中,ZonedDateTime.of 方法传入年、月、日、小时、分、秒、纳秒以及时区作为参数。除时区(ZoneId)外,其他字段均为 int 类型,即无法使用 Month 枚举。

输出如下:

  1. Sunday, March 11, 2018 3:30:00 AM EDT

可以看到,程序将时间从凌晨 2 时 30 分调整为凌晨 3 时 30 分。