解析年份时出现 java.time.format.DateTimeParseException

diw*_*att 4 java java-8

使用的 jdk:1.8

不知道是什么问题,配置格式是有效的,输入时间也是有效的,真的很困惑是什么问题。

public class Test {

    public static void main(String[] args) {
        String configuredFormat = "yyyyMMddHHmmssSSS";
        String inputTime = "20200203164553123";

        DateTimeFormatter dt = DateTimeFormatter.ofPattern(configuredFormat);
        DateTimeFormatter strictTimeFormatter = dt.withResolverStyle(ResolverStyle.STRICT);
        try {
            LocalTime.parse(inputTime, strictTimeFormatter);
            System.out.println("success");
        } catch (DateTimeParseException | NullPointerException e) {
            e.printStackTrace();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我得到的例外:

java.time.format.DateTimeParseException: Text '20200203164553123' could not be parsed at index 0
    at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
    at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
    at java.time.LocalTime.parse(LocalTime.java:441)
    at com.Test.main(Test.java:20)
Run Code Online (Sandbox Code Playgroud)

Mic*_*ael 10

幸运的是,有一个确切的错误报告,它使用与您正在尝试的完全相同的模式。谁能比 JDK 维护者更好地解释?

JDK-8031085

解决方法

DateTimeFormatter dtf = new
DateTimeFormatterBuilder()
    .appendPattern("yyyyMMddHHmmss")
    .appendValue(ChronoField.MILLI_OF_SECOND,3)
    .toFormatter()
Run Code Online (Sandbox Code Playgroud)

相邻值解析通常是一个难题。它旨在处理第一个元素是可变宽度(年)而所有其他元素是固定宽度(月、日等)的情况。但是,“S”模式字母是一个分数,而不是一个值。具体来说,分数可以是可变宽度 - 多于或少于三位数是可能的选择。鉴于可变宽度年份和可变宽度毫秒的一般情况,无法确定这两个字段中的哪一个是可变的。

话虽如此,实现(和 javadoc)并没有如我所愿。DateTimeFormatter 中“分数”的描述描述了严格和宽松模式下的操作,但在使用 DateTimeFormatter.ofPattern() 时无法访问严格或宽松模式。这是一个文档错误,应该通过删除严格与宽松的讨论来修复。

然而更糟糕的是,SSS 模式因此在宽松模式适合时使用了严格模式。就目前而言,DateTimeFormatter.ofPattern("hhmmss.SSS") 需要三位数的毫秒数,而最初打算要求 0 到 9(宽松的行为)。

我尝试将整个 DateTimeFormatter.ofPattern() 方法更改为使用宽松的解析,并且它没有破坏任何测试(这本身就很糟糕)。这可能是一个有效的修复,但前提是包含在 JDK 8 中,因为一旦人们适应了严格的解析,就很难让它变得宽容。

鉴于当前的实现需要三位数的 SSS,因此相邻值解析不适用是非常令人惊讶的。