如何使用 DateTimeFormatter 统一日期格式

How to unify date format using DateTimeFormatter

我需要将不同的时间格式解析为 BASIC_ISO_DATE。目前,有 4 种类型的日期格式:

需要解析为20161001打印出来,默认日01,默认月Jan。示例:

如何使用 DateTimeFormatter 来实现这一点?

尝试这样的事情:

public LocalDate parse(String str) {
   // Might want to add some checks here...
   String text = (str.replaceAll("[\-T]", "") + "0101").substring(0, 8);
   return LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyyMMdd"));
}

您可以构建自己的 DateTimeFormatter

DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
  .appendOptional(DateTimeFormatter.ISO_ZONED_DATE_TIME)
  .appendOptional(DateTimeFormatter.ISO_LOCAL_DATE)
  .appendOptional(new DateTimeFormatterBuilder()
    .appendValue(ChronoField.YEAR, 4)
    .optionalStart()
      .appendValue(ChronoField.MONTH_OF_YEAR)
    .optionalEnd()
    .appendLiteral('T')
    .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1L)
    .parseDefaulting(ChronoField.DAY_OF_MONTH, 1L)
    .toFormatter())
  .toFormatter();
String[] strings = {"2016-10-01", "2016T", "201610T", "2016-02-07T22:03:39.937Z"};
for (String s : strings) {
  System.out.println(LocalDate.parse(s, dateTimeFormatter)
        .format(DateTimeFormatter.BASIC_ISO_DATE));
}

如果您只想要一个 DateTimeFormatter 是经过深思熟虑的,非常适合添加更多可能的格式或以其他方式更改要求。

我认为一个简单的替代方法是使用三个或四个 DateTimeFormatter 对象并依次尝试直到一个有效。

对于 2016T,您可以像 Flown 的回答那样使用 parseDefaulting(),或者只是解析为 Year 然后使用 .atDay(1)。与 201610T 类似:一种选择是解析为 YearMonth 并使用其 atDay().

我的解决方案可能不太优雅,但可能更易读。

只是为了补充 (顺便说一句,效果很好),您还可以使用可选模式(由 [] 分隔):

DateTimeFormatter parser = new DateTimeFormatterBuilder()
    // optional ISO8601 date/time and offset
    .appendOptional(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
    // optional yyyy-MM-dd or yyyyT or yyyyMMT
    .appendPattern("[yyyy-MM-dd][yyyy'T'][yyyyMM'T']")
    // default day is 1
    .parseDefaulting(ChronoField.DAY_OF_MONTH, 1L)
    // default month is January
    .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1L)
    // create formatter
    .toFormatter();

这完全相同。您可以选择哪个更清晰或更易于维护。如果有 lots 不同的模式,使用 [] 可能最终会更加混乱,IMO。

请注意,我使用 ISO_OFFSET_DATE_TIME 而不是 ISO_ZONED_DATE_TIME。唯一的区别是 ISO_ZONED_DATE_TIME 最后也接受时区名称(如 [Europe/London]),而 ISO_OFFSET_DATE_TIME 不接受。 Check the javadoc 获取更多信息。