【问题标题】:DateTimeFormatter Accepting Multiple Dates and Converting to One (java.time library)DateTimeFormatter 接受多个日期并转换为一个(java.time 库)
【发布时间】:2017-11-19 21:22:30
【问题描述】:

我正在尝试编写一个DateTimeFormatter,它允许我接受多种不同的String 格式,然后将String 格式转换为特定类型。由于项目的范围和已经存在的代码,我不能使用不同类型的格式化程序。

例如,我想接受MM/dd/yyyyyyyy-MM-dd'T'HH:mm:ss,但是当我打印时,我只想打印为MM/dd/yyyy 格式,并在我调用LocalDate.format(formatter); 时将其设置为格式

有人可以就如何使用java.time.format.*; 提出建议

这是我在org.joda 中的做法:

// MM/dd/yyyy format
DateTimeFormatter monthDayYear = DateTimeFormat.forPattern("MM/dd/yyyy");
// array of parsers, with all possible input patterns
DateTimeParser[] parsers = {
        // parser for MM/dd/yyyy format
        monthDayYear.getParser(),
        // parser for yyyy-MM-dd'T'HH:mm:ss format
        DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss").getParser()
};
DateTimeFormatter parser = new DateTimeFormatterBuilder()
    // use the monthDayYear formatter for output (monthDayYear.getPrinter())
    // and parsers array for input (parsers)
    .append(monthDayYear.getPrinter(), parsers)
    // create formatter (using UTC to avoid DST problems)
    .toFormatter()
    .withZone(DateTimeZone.UTC);

我没有在网上找到一个好的/有效的例子。

【问题讨论】:

    标签: java datetime datetime-format java-time datetime-parsing


    【解决方案1】:

    我已经使用 JDK 1.8.0_131 for Mac OS XJDK 1.8.0111 for Windows 对此进行了测试(两者都有效)。

    我创建了一个带有可选部分(由[] 分隔)的DateTimeFormatter,以解析两种情况(MM/dd/yyyyyyyy-MM-dd'T'HH:mm:ss)。

    同样的格式化程序适用于您的案例 (LocalDate),但下面有一些注意事项。

    // parse both formats (use optional section, delimited by [])
    DateTimeFormatter parser = DateTimeFormatter.ofPattern("[MM/dd/yyyy][yyyy-MM-dd'T'HH:mm:ss]");
    
    // parse MM/dd/yyyy
    LocalDate d1 = LocalDate.parse("10/16/2016", parser);
    // parse yyyy-MM-dd'T'HH:mm:ss
    LocalDate d2 = LocalDate.parse("2016-10-16T10:20:30", parser);
    
    // parser.format(d1) is the same as d1.format(parser)
    System.out.println(parser.format(d1));
    System.out.println(parser.format(d2));
    

    输出是:

    2016 年 10 月 16 日
    2016 年 10 月 16 日


    PS:这仅适用于LocalDate。如果我尝试使用时间字段(如LocalDateTime)格式化对象,则会使用两种格式:

    System.out.println(parser.format(LocalDateTime.now()));
    

    打印出来:

    06/18/20172017-06-18T07:40:55

    请注意,它使用两种模式进行格式化。我的猜测是格式化程序检查对象是否在每个可选部分中都有字段。由于LocalDate 没有时间字段(小时/分钟/秒),第二个模式失败,它只打印第一个(MM/dd/yyyy)。但是LocalDateTime有所有的时间字段,而且两种模式都是有效的,所以都是用来格式化的。

    我的结论是:这不是一个通用的解决方案(如 Joda-Time 的版本),它更像是一个“幸运”案例,其中所涉及的模式创造了所需的情况。但我不会在所有情况下都依赖它。

    不管怎样,如果你只使用LocalDate,你可以尝试使用这段代码。但是,如果您正在使用其他类型,那么您可能必须使用 another 格式化程序来输出,如下所示:

    // parser/formatter for month/day/year
    DateTimeFormatter mdy = DateTimeFormatter.ofPattern("MM/dd/yyyy");
    // parser for both patterns
    DateTimeFormatter parser = new DateTimeFormatterBuilder()
        // optional MM/dd/yyyy
        .appendOptional(mdy)
        // optional yyyy-MM-dd'T'HH:mm:ss (use built-in formatter)
        .appendOptional(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
        // create formatter
        .toFormatter();
    
    // parse MM/dd/yyyy
    LocalDate d1 = LocalDate.parse("10/16/2016", parser);
    // parse yyyy-MM-dd'T'HH:mm:ss
    LocalDate d2 = LocalDate.parse("2016-10-16T10:20:30", parser);
    
    // use mdy to format
    System.out.println(mdy.format(d1));
    System.out.println(mdy.format(d2));
    
    // format object with time fields: using mdy formatter to avoid multiple pattern problem
    System.out.println(mdy.format(LocalDateTime.now()));
    

    输出是:

    2016 年 10 月 16 日
    2016 年 10 月 16 日
    2017 年 6 月 18 日

    【讨论】:

    • 坦克!应该在顶部的那个
    【解决方案2】:

    解析部分可写,已添加到ThreeTen-Extra库中。相关代码为here,为清楚起见,将其包含在下方。关键技巧是使用parseUnresolved() 找出正确的格式:

    public static <T> T parseFirstMatching(CharSequence text, TemporalQuery<T> query, DateTimeFormatter... formatters) {
        Objects.requireNonNull(text, "text");
        Objects.requireNonNull(query, "query");
        Objects.requireNonNull(formatters, "formatters");
        if (formatters.length == 0) {
            throw new DateTimeParseException("No formatters specified", text, 0);
        }
        if (formatters.length == 1) {
            return formatters[0].parse(text, query);
        }
        for (DateTimeFormatter formatter : formatters) {
            try {
                ParsePosition pp = new ParsePosition(0);
                formatter.parseUnresolved(text, pp);
                int len = text.length();
                if (pp.getErrorIndex() == -1 && pp.getIndex() == len) {
                    return formatter.parse(text, query);
                }
            } catch (RuntimeException ex) {
                // should not happen, but ignore if it does
            }
        }
        throw new DateTimeParseException("Text '" + text + "' could not be parsed", text, 0);
    }
    

    不幸的是,没有办法编写单个 DateTimeFormatter 来支持灵活的解析和使用 Joda-Time 的特定输出格式进行打印。

    【讨论】:

      【解决方案3】:

      Answer by Andreas 是正确的,应该被接受。

      检查字符串长度

      作为替代方案,您可以简单地测试字符串的长度并应用两个格式化程序之一。

      DateTimeFormatter fDateOnly = DateTimeFormatter.ofPattern( "MM/dd/uuuu" ) ;
      DateTimeFormatter fDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME ;
      
      LocalDate ld = null ;
      if( input.length() == 10 ) {
          try {
              ld = LocalDate.parse( input , fDateOnly ) ;
          } catch (DateTimeParseException  e ) {
              …
          }
      } else if ( input.length() == 19 ) {
          try {
              LocalDateTime ldt = LocalDateTime.parse( input , fDateTime ) ;
              ld = ldt.toLocalDate() ;
          } catch (DateTimeParseException  e ) {
              …
          }
      } else {
          // Received unexpected input.
          …
      }
      
      String output = ld.format( fDateOnly ) ;
      

      请注意,您可以让 java.time 在生成表示日期时间值的字符串时自动本地化,而不是硬编码特定格式。见DateTimeFormatter.ofLocalizedDate

      【讨论】:

        【解决方案4】:

        你问的是不可能的。

        DateTimeFormatter 是一个final 类,所以你不能子类化它来实现你自己的行为。

        构造函数是包私有的,所以你不能自己调用​​它。创建DateTimeFormatter 的唯一方法是使用DateTimeFormatterBuilder。请注意,用于创建 DateTimeFormatterstatic 辅助方法在内部使用 DateTimeFormatterBuilder,例如

        public static DateTimeFormatter ofPattern(String pattern) {
            return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
        }
        

        DateTimeFormatterBuilder 也是一个final 类,不能被子类化,并且它没有提供任何方法来提供多种可供使用的替代格式,如您所愿。

        简而言之,DateTimeFormatter 已关闭,无法扩展。如果您的代码只能使用DateTimeFormatter,那么您就不走运了。

        【讨论】:

        • 我希望能够允许多种不同类型的输入。我知道对于 org.joda,我可以按照上面列出的方式执行此操作。
        • 我知道这在 org.joda 中肯定是可能的。Java 库是否直接不如 joda 库那么有用?
        • @AlexKornhauser 它并不逊色于 Joda-Time。只是略有不同。我相信编写 Joda-Time 的一些人是编写 java.time 课程的小组的一员,他们应用了从 Joda-Time 学到的经验,这意味着它实际上比 Joda-Time 更好。 Joda-Time website 甚至鼓励人们从 Joda-Time 切换到 java.timeJoda-Time 是 Java SE 8 之前 Java 的事实上的标准日期和时间库。用户现在要求迁移到java.time (JSR-310)。
        猜你喜欢
        • 2017-11-17
        • 1970-01-01
        • 2021-02-10
        • 2022-11-14
        • 1970-01-01
        • 1970-01-01
        • 2011-11-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多