如果删除空间,DateTimeFormatter解析带有可选时间部分的字符串将失败

时间:2018-03-19 08:51:09

标签: java java-8 java-time

my other question on how to parse date-only strings as LocalDateTime之后,尝试使用模式yyyyMMdd [HHmmss]解析字符串20120301122133时出现错误。奇怪的是,使用模式yyyyMMdd [HHmmss]解析20120301 122133非常有效。

所以这段代码工作正常

LocalDateTime.parse(
     "19940513 230000", 
     new DateTimeFormatterBuilder()
        .appendPattern("yyyyMMdd[ HHmmss]")
        .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
        .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)   
        .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
        .toFormatter()
)

这一次失败

LocalDateTime.parse(
    "19940513230000", 
    new DateTimeFormatterBuilder()
        .appendPattern("yyyyMMdd[HHmmss]")
        .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) 
        .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)         
        .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
        .toFormatter()
)

我应该如何解析格式为yyyyMMdd [HHmmss]的字符串,即格式为yyyyMMddHHmmss,并使用java 8时间API提供可选的时间部分?

解析模式是可配置选项,因此仅在运行时才知道。所以我不能,例如用硬编码的DateTimeFormatterBuilder调用替换String模式。

3 个答案:

答案 0 :(得分:1)

问题在于模式表达" yyyy"并不表示固定的四位数年份,而是指示至少4位数(或更多,因此解析器是贪婪的)。但你可以这样做:

LocalDateTime ldt =
    LocalDateTime.parse(
        "19940513230000",
        new DateTimeFormatterBuilder()
            .appendValue(ChronoField.YEAR, 4)
            .appendPattern("MMdd[HHmmss]")
            .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
            .toFormatter());
System.out.println(ldt); // 1994-05-13T23:00

答案 1 :(得分:1)

    System.out.println(LocalDateTime.parse(
            "19940513230000",
            new DateTimeFormatterBuilder()
                .appendPattern("[uuuuMMddHHmmss][uuuuMMdd]")
                .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) 
                .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)         
                .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
                .toFormatter()
        ));

打印:

  

1994-05-13T23:00

如果我尝试使用日期"19940513"解析一个字符串,我得到

  

1994-05-13T00:00

它也适用于yyyy而不是uuuu。假设您的所有年份都处于这个时代(第1年或更晚),那么您使用哪一年并没有任何区别。通常uuuu也会接受负面年份,0表示1 BCE,-1表示2 BCE,等等。

答案 2 :(得分:1)

这是因为年份没有固定值,通常限制在19位数。

如果您创建以下格式化程序(不需要分钟和秒)并使用toString()方法:

new DateTimeFormatterBuilder()
        .appendPattern("yyyyMMdd[ HHmmss]")
        .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
        .toFormatter()
        .toString();

您可以看到以下内容:

  

“值( YearOfEra,4,19,EXCEEDS_PAD )值(MonthOfYear,2)值(DayOfMonth,2)[''Value(HourOfDay,2)Value(MinuteOfHour,2)Value( SecondOfMinute,2)] java.time.format.DateTimeFormatterBuilder$DefaultValueParser@32eff876"

在这里,您可以看到YearOfEra的最小宽度为4,最大值为19。

您可以使用Meno或Ole的答案之一。

但是,如果您需要接收格式和日期作为参数,并且希望能够以更简单的方式指定日期格式(例如yyyyMMdd[HHmmSS]而不是[...][...]),则可以预处理其中一个to值(日期格式)。

您可以创建'dinamically'格式化程序,因此每yyyy只会被解释为4位数年份。

自定义格式构建器可能类似于(可以改进):

public static DateTimeFormatter createFixed4DigitYearFormatter(String format) {
    DateTimeFormatterBuilder formatBuilder = new DateTimeFormatterBuilder();
    Arrays.stream(format.split("yyyy", -1))
            .flatMap(cur -> Stream.of("yyyy", cur)).skip(1)
            .filter(str -> !str.isEmpty())
            .forEach(pattern -> {
                if ("yyyy".equals(pattern)) formatBuilder
                        .appendValue(ChronoField.YEAR_OF_ERA, 4);
                else formatBuilder.appendPattern(pattern);
            });
    return formatBuilder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0).toFormatter();
}

此格式化程序按字符串"yyyy"拆分格式,然后将每个非"yyyy"添加为模式(使用appendPattern(..)),并将"yyyy"添加为值类型YEAR_OF_ERA,固定4位数(appendValue(..))。

最后,您可以使用多种格式的格式化程序:

System.out.println(LocalDateTime.parse("19940513230000",
        createFixed4DigitYearFormatter("yyyyMMdd[HHmmss]"))); // 1994-05-13T23:00
System.out.println(LocalDateTime.parse("19940513",
        createFixed4DigitYearFormatter("yyyyMMdd[HHmmss]"))); // 1994-05-13T00:00
System.out.println(LocalDateTime.parse("1994-05-13 23:00:00",
        createFixed4DigitYearFormatter("yyyy-MM-dd[ HH:mm:ss]"))); // 1994-05-13T23:00
System.out.println(LocalDateTime.parse("1994-05-13",
        createFixed4DigitYearFormatter("yyyy-MM-dd[ HH:mm:ss]"))); // 1994-05-13T00:00