繁体   English   中英

Java 8 - ZonedDateTime 的 DateTimeFormatter 和 ISO_INSTANT 问题

[英]Java 8 - DateTimeFormatter and ISO_INSTANT issues with ZonedDateTime

所以我希望这段代码可以在新的 Java 8 日期/时间包下工作,因为它所做的只是使用相同的内置 DateTimeFormatter 实例 (ISO_INSTANT) 将给定的 ZonedDateTime 转换为字符串并返回:

ZonedDateTime now = ZonedDateTime.now();
System.out.println(ZonedDateTime.parse(
    now.format(DateTimeFormatter.ISO_INSTANT),
    DateTimeFormatter.ISO_INSTANT));

但显然它没有:

Exception in thread "main" java.time.format.DateTimeParseException: Text '2014-09-01T19:37:48.549Z' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {MilliOfSecond=549, NanoOfSecond=549000000, MicroOfSecond=549000, InstantSeconds=1409600268},ISO of type java.time.format.Parsed
    at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1918)
    at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1853)
    at java.time.ZonedDateTime.parse(ZonedDateTime.java:597)

我已经看过这个条目,但它对我没有帮助,因为需要一个 ZonedDateTime 对象而不是本地对象,还因为我已经安装了 8u20: 无法使用 Java 8 中的 DateTimeFormatter 和 ZonedDateTime 从 TemporalAccessor 获取 ZonedDateTime

有人知道这里发生了什么吗?

ISO_INSTANT格式化程序记录在此处- “这是一种特殊情况格式化程序,旨在允许即时的人类可读形式”。 因此,此格式化程序旨在用于Instant而不是ZonedDateTime

格式化

格式化时, ISO_INSTANT可以格式化任何可以提供ChronoField.INSTANT_SECONDSChronoField.NANO_OF_SECOND时间对象。 InstantZonedDateTime都可以提供这两个字段,因此都可以工作:

// works with Instant
Instant instant = Instant.now();
System.out.println(DateTimeFormatter.ISO_INSTANT.format(instant));

// works with ZonedDateTime 
ZonedDateTime zdt = ZonedDateTime.now();
System.out.println(zdt.format(DateTimeFormatter.ISO_INSTANT));

// example output
2014-09-02T08:05:23.653Z

解析

解析时, ISO_INSTANT只会产生ChronoField.INSTANT_SECONDSChronoField.NANO_OF_SECOND 可以从这两个字段构建Instant ,但ZonedDateTime需要ZoneId

要解析ZonedDateTime ,必须存在时区ZoneId 时区可以 (a) 从字符串中解析,或 (b) 指定给格式化程序(使用 JDK 8u20):

// option a - parsed from the string
DateTimeFormatter f = DateTimeFormatter.ISO_DATE_TIME;
ZonedDateTime zdt = ZonedDateTime.parse("2014-09-02T08:05:23.653Z", f);

// option b - specified in the formatter - REQUIRES JDK 8u20 !!!
DateTimeFormatter f = DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.systemDefault());
ZonedDateTime zdt = ZonedDateTime.parse("2014-09-02T08:05:23.653Z", f);

请参阅ISO_ZONED_DATE_TIMEISO_OFFSET_DATE_TIMEISO_DATE_TIME (这三个中的任何一个ZonedDateTime用于解析ZonedDateTime而不指定withZone() )。

概括

ISO_INSTANT格式化程序是一种特殊情况格式化程序,旨在与Instant 如果您使用的是ZonedDateTime ,则应使用不同的格式化程序,例如ISO_DATE_TIMEISO_ZONED_DATE_TIME

我不确定,但这可能是 Java 8 中的一个错误。也许它本打算以这种方式行事,但我认为我要向您提出的解决方法应该是默认行为(当没有 ZoneId 时)指定,只需采用系统默认值):

ZonedDateTime now = ZonedDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT
    .withZone(ZoneId.systemDefault());
System.out
    .println(ZonedDateTime.parse(now.format(formatter), formatter));

OpenJDK 中修复了一个类似的错误: JDK-8033662 - 但它只是相似,不完全相同。

我不知道这是否是预期的行为(可能是),但从技术上讲, ISO_INSTANT格式化程序不包含时区。 如果您尝试使用DateTimeFormatter.ISO_ZONED_DATE_TIME格式化程序,您将得到您期望的结果。

如果您使用的是Java 1.8 中的org.threeten.bp ,这里有一个关于处理 String to Date 的org.threeten.bp,反之亦然。

日期到字符串

val date = Date()
val calendar = Calendar.getInstance().apply { time = date }
val zonedDateTime = DateTimeUtils.toZonedDateTime(calendar)
val formattedDate = DateTimeFormatter.ISO_INSTANT.format(zonedDateTime)

字符串到日期

val dateString = "2020-03-04T09:04:43.835Z"
val dateInstant = Instant.from(DateTimeFormatter.ISO_INSTANT.parse(dateString))
DateTimeUtils.toDate(dateInstant)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM