为什么使用 DateTimeFormatter 进行日期解析会根据 Java 版本给出不同的结果
Why date parsing using DateTimeFormatter gives different result depending on Java version
相同的日期解析代码显示不同的结果,具体取决于我使用的是 Java 8 还是 Java 11。
以下是重现问题的代码示例:
public static void main(String[] args) {
DateTimeFormatterBuilder dfBuilder = new DateTimeFormatterBuilder()
.appendPattern("uuuu-M-d")
.optionalStart()
.optionalStart().appendLiteral(' ').optionalEnd()
.optionalStart().appendLiteral('T').optionalEnd()
.appendValue(ChronoField.HOUR_OF_DAY)
.optionalStart()
.appendLiteral(':')
.appendValue(ChronoField.MINUTE_OF_HOUR)
.optionalStart()
.appendLiteral(':')
.appendValue(ChronoField.SECOND_OF_MINUTE)
.optionalStart()
.appendFraction(ChronoField.NANO_OF_SECOND, 1, 9, true)
.optionalEnd()
.optionalEnd()
.optionalEnd()
.appendPattern("[XXXXX][XXXX][XXX][XX][X]")
.optionalEnd();
DateTimeFormatter df = dfBuilder.toFormatter(Locale.FRANCE).withZone(ZoneId.of("Europe/Paris"));
TemporalAccessor temporalAccessor = df.parse("1970-01-01T00:00:00.00Z");
Instant instantTime = Instant.from(temporalAccessor);
long epochTimestamp = instantTime.getEpochSecond();
System.out.println(epochTimestamp);
}
在 java 8 上输出为 -3600。
在 Java 11 时输出为 0(这是我期望的值)。
根据我的分析,起源是“withZone()”方法的使用,如果我删除它,它会按预期方式为包含区域数据的日期工作。
但同时此行为在所有 DateTimeFormatter 上都不相同:例如,如果我使用此格式化程序:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd HH:mm:ss")
.appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true)
.optionalStart()
.appendZoneId()
.optionalEnd()
.toFormatter()
.withZone(ZoneId.of("Europe/Paris"));
如果日期内存在 zoneId,则会很好地考虑到它(尽管调用了“withZone()”)。
所以我想第一个格式化程序有问题, Java 11 上的默认行为有助于它在 Java 11 上按预期工作,但在 Java 8 上却没有?
此外,我明确表示我的目标是让它在 Java 8.
上运行
已知错误
这是 Java 8 中的一个已知错误。Parsing with DateTimeFormatter.withZone does not behave as described in javadocs。
有趣的是,我 link Java 错误系统中的错误报告没有提到修复版本。它确实提到该错误不在 JDK 9-ea 中,但是:
To reproduce the issue, run the attached test case. It fails on JDK
8u121 but passes in JDK 9-ea.
相同的日期解析代码显示不同的结果,具体取决于我使用的是 Java 8 还是 Java 11。
以下是重现问题的代码示例:
public static void main(String[] args) {
DateTimeFormatterBuilder dfBuilder = new DateTimeFormatterBuilder()
.appendPattern("uuuu-M-d")
.optionalStart()
.optionalStart().appendLiteral(' ').optionalEnd()
.optionalStart().appendLiteral('T').optionalEnd()
.appendValue(ChronoField.HOUR_OF_DAY)
.optionalStart()
.appendLiteral(':')
.appendValue(ChronoField.MINUTE_OF_HOUR)
.optionalStart()
.appendLiteral(':')
.appendValue(ChronoField.SECOND_OF_MINUTE)
.optionalStart()
.appendFraction(ChronoField.NANO_OF_SECOND, 1, 9, true)
.optionalEnd()
.optionalEnd()
.optionalEnd()
.appendPattern("[XXXXX][XXXX][XXX][XX][X]")
.optionalEnd();
DateTimeFormatter df = dfBuilder.toFormatter(Locale.FRANCE).withZone(ZoneId.of("Europe/Paris"));
TemporalAccessor temporalAccessor = df.parse("1970-01-01T00:00:00.00Z");
Instant instantTime = Instant.from(temporalAccessor);
long epochTimestamp = instantTime.getEpochSecond();
System.out.println(epochTimestamp);
}
在 java 8 上输出为 -3600。
在 Java 11 时输出为 0(这是我期望的值)。
根据我的分析,起源是“withZone()”方法的使用,如果我删除它,它会按预期方式为包含区域数据的日期工作。
但同时此行为在所有 DateTimeFormatter 上都不相同:例如,如果我使用此格式化程序:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd HH:mm:ss")
.appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true)
.optionalStart()
.appendZoneId()
.optionalEnd()
.toFormatter()
.withZone(ZoneId.of("Europe/Paris"));
如果日期内存在 zoneId,则会很好地考虑到它(尽管调用了“withZone()”)。 所以我想第一个格式化程序有问题, Java 11 上的默认行为有助于它在 Java 11 上按预期工作,但在 Java 8 上却没有? 此外,我明确表示我的目标是让它在 Java 8.
上运行已知错误
这是 Java 8 中的一个已知错误。Parsing with DateTimeFormatter.withZone does not behave as described in javadocs。
有趣的是,我 link Java 错误系统中的错误报告没有提到修复版本。它确实提到该错误不在 JDK 9-ea 中,但是:
To reproduce the issue, run the attached test case. It fails on JDK 8u121 but passes in JDK 9-ea.