【问题标题】:Java 8 Date equivalent to Joda's DateTimeFormatterBuilder with multiple parser formats?Java 8 Date 等效于 Joda 的 DateTimeFormatterBuilder,具有多种解析器格式?
【发布时间】:2016-07-11 08:23:11
【问题描述】:

我目前有一个 Joda 日期解析器,它使用 DateTimeFormatterBuilder 和我可能收到的六种不同日期格式。

我正在迁移到 Java 8 的 Date 例程,但没有看到等效的例程。

如何使用 Java 8 Dates 做这样的事情?

DateTimeParser[] parsers = { 
    DateTimeFormat.forPattern( "yyyy/MM/dd HH:mm:ss.SSSSSS" ).getParser() ,
    DateTimeFormat.forPattern( "yyyy-MM-dd HH:mm:ss" ).getParser() ,
    DateTimeFormat.forPattern( "ddMMMyyyy:HH:mm:ss.SSS Z" ).getParser() ,
    DateTimeFormat.forPattern( "ddMMMyyyy:HH:mm:ss.SSS" ).getParser() ,
    DateTimeFormat.forPattern( "ddMMMyyyy:HH:mm:ss.SSSSSS" ).getParser() ,
    DateTimeFormat.forPattern( "yyyy-MM-dd HH:mm:ss.SSS" ).getParser() 
};

DateTimeFormatter dateTimeFormatterInput = new DateTimeFormatterBuilder()
     .append( null, parsers ).toFormatter();

【问题讨论】:

  • 您可能会观察到性能方面的严重下降 - 特别是如果您计划通过异常处理实现替换。根据我自己的测试,Joda-Time 更快。另请参阅JDK-issue

标签: java jodatime java-time


【解决方案1】:

没有直接的工具可以做到这一点,但您可以使用可选部分。可选部分括在方括号[] 内。这允许要解析的字符串的整个部分丢失。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern(""
    + "[yyyy/MM/dd HH:mm:ss.SSSSSS]"
    + "[yyyy-MM-dd HH:mm:ss[.SSS]]"
    + "[ddMMMyyyy:HH:mm:ss.SSS[ Z]]"
);

此格式化程序为您拥有的三种主要模式定义了 3 个重要的可选部分。它们中的每一个都在其自己的可选部分中。

工作演示代码:

public static void main(String[] args) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(""
        + "[yyyy/MM/dd HH:mm:ss.SSSSSS]"
        + "[yyyy-MM-dd HH:mm:ss[.SSS]]"
        + "[ddMMMyyyy:HH:mm:ss.SSS[ Z]]"
    , Locale.ENGLISH);
    System.out.println(LocalDateTime.parse("2016/03/23 22:00:00.256145", formatter));
    System.out.println(LocalDateTime.parse("2016-03-23 22:00:00", formatter));
    System.out.println(LocalDateTime.parse("2016-03-23 22:00:00.123", formatter));
    System.out.println(LocalDateTime.parse("23Mar2016:22:00:00.123", formatter));
    System.out.println(LocalDateTime.parse("23Mar2016:22:00:00.123 -0800", formatter));
}

【讨论】:

  • 如果区域设置也不同,则此代码将不起作用。
  • 请注意,您不能将其用于格式化,只能用于解析。使用此类格式化程序进行格式化时,您将获得所有格式。我猜如果你想要一些可选格式和一个“默认”显示,你必须使用两个不同的 DateTimeFormatter 实例 - 一个使用用于解析的可选格式,一个只使用默认显示格式。跨度>
  • 不幸的是 .appendPattern("yyyyMMdd'T'HHmmssZ").toFormatter(); 不起作用,但 "[yyyyMMdd'T'HHmmssZ]" 起作用
  • 我应该注意到模式的顺序很重要,它们应该从最长到最短,从最严格到最不严格。这不起作用:DateTimeFormatter formatter = DateTimeFormatter.ofPattern("" + "[yyyy-MM-dd]" + "[yyyy-MM-dd HH:mm:ss]" + "[dd/MM/yyyy]" + "[dd/MM/yyyy HH:mm:ss]") 这是正确的:DateTimeFormatter.ofPattern("" + "[yyyy-MM-dd HH:mm:ss]" + "[dd/MM/yyyy HH:mm:ss]" + "[yyyy-MM-dd]" + "[dd/MM/yyyy]" )
【解决方案2】:

作为 Tunaki 的替代答案,您还可以使用 DateTimeFormatterBuilder:

DateTimeFormatter dateFormatter = new DateTimeFormatterBuilder()
  .appendPattern("[yyyy]")
  .appendPattern("[M/d/yyyy]")
  .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
  .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
  .toFormatter()

【讨论】:

  • 我没有让它与多个附加一起工作。我创建了一个 DateTimeFormaters 列表和一个带有 try/catch 的循环。
  • @Avec try-catch 应该用于处理异常而不是用于预期的输入。但是您是对的,建议的代码不正确。只能通过 DateTimeFormatterBuilder 的 appendOptional 方法使用多个格式化程序。
【解决方案3】:

当代码需要以可配置的方式接受不同的模式时,使用流迭代@Tunaki 的解决方案:

DateTimeFormatter dateTimeFormatter = dateFormats.stream()
        .map(DateTimeFormatter::ofPattern)
        .reduce(new DateTimeFormatterBuilder(), 
                DateTimeFormatterBuilder::appendOptional, 
                (f1, f2) -> f1.append(f2.toFormatter()))
        .toFormatter();

在这种情况下,我不关心 reducer 的组合器部分,但我在签名中需要它,所以我使组合器正确。

此代码实际上相当于将上述模式(yyyy/MM/dd HH:mm:ss.SSSSSSyyyy-MM-dd HH:mm:ss[.SSS]ddMMMyyyy:HH:mm:ss.SSS[ Z])馈送到流中:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .appendOptional(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSSSSS")
    .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSS]"
    .appendOptional(DateTimeFormatter.ofPattern("ddMMMyyyy:HH:mm:ss.SSS[ Z]")
    .toFormatter();

【讨论】:

    【解决方案4】:

    根据@Brice 的回答,我编写了以下最适合我的方法

    private LocalDate parseDate(String date) {
        DateTimeFormatter yearFormat = new DateTimeFormatterBuilder()
                .appendPattern("yyyy")
                .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
                .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
                .toFormatter();
        DateTimeFormatter yearAndMonthFormat = new DateTimeFormatterBuilder()
                .appendPattern("yyyy-MM")
                .parseDefaulting(ChronoField.DAY_OF_MONTH,1)
                .toFormatter();
    
        DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                .appendOptional(DateTimeFormatter.ISO_DATE)
                .appendOptional(yearAndMonthFormat)
                .appendOptional(yearFormat)
                .toFormatter();
    
        LocalDate result = LocalDate.parse(date, formatter);
    
        return result;
    }
    

    注意添加到最后一个的可选格式化程序的顺序。反向顺序仅适用于“yyyy”类型的日期。建议的顺序允许我解析以下所有内容:

    • 2014
    • 2014-10
    • 2014-03-15

    只是为某人可能有类似用例的情况添加。

    【讨论】:

      【解决方案5】:

      这是我最终想出的。它处理三种不同的主要格式,每种格式都有多个次要格式差异(例如允许使用 - 或 / 分隔符)以及处理可变数量的微秒(从 0 到 6):

      private static final DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
              .parseCaseInsensitive()
              .appendPattern( "[ddMMMyyyy:HH:mm:ss" )
              .optionalStart()
              .appendFraction( ChronoField.MICRO_OF_SECOND , 1 , 6 , true )
              .optionalEnd()
              .appendPattern( "[ ][Z][X]]" )
              .appendPattern( "[yyyy[-][/]MM[-][/]dd['T'][ ]HH:mm[:][.]ss" )
              .optionalStart()
              .appendFraction( ChronoField.MICRO_OF_SECOND , 1 , 6 , true )
              .optionalEnd()
              .appendPattern( "[Z][X]]" )
              .appendPattern( "[EEE, dd MMM yyyy HH:mm:ss zzz]" )
              .toFormatter();
      

      【讨论】:

        猜你喜欢
        • 2011-03-19
        • 2021-08-10
        • 1970-01-01
        • 2020-01-12
        • 1970-01-01
        • 2013-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多