DateTimeFormatter接受多个日期并转换为一个日期(java.time库)

时间:2017-06-17 03:18:15

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

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

例如,我想接受MM/dd/yyyy以及yyyy-MM-dd'T'HH:mm:ss,但是当我打印时,我只想打印到MM/dd/yyyy格式,并在我调用{{LocalDate.format(formatter);格式时使用格式1}}

有人可以通过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);

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

4 个答案:

答案 0 :(得分:9)

我用 JDK 1.8.0_131 for Mac OS X JDK 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,则可以尝试使用此代码。但是如果您正在使用其他类型,那么您可能必须使用另一个格式化程序来输出,如下所示:

// 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日

答案 1 :(得分: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);
}

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

答案 2 :(得分:1)

你提出的问题是不可能的。

DateTimeFormatter是一个final类,因此您不能将其子类化以实现自己的行为。

构造函数是包私有的,因此您无法自己调用它。创建DateTimeFormatter的唯一方法是使用DateTimeFormatterBuilder。请注意,用于创建static的{​​{1}}辅助方法在内部使用DateTimeFormatter,例如

DateTimeFormatterBuilder

public static DateTimeFormatter ofPattern(String pattern) { return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(); } 也是DateTimeFormatterBuilder类,无法进行子类化,也没有提供任何方法来提供多种替代格式供您使用。

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

答案 3 :(得分:1)

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