Java 8 - 使用ZonedDateTime的DateTimeFormatter和ISO_INSTANT问题

时间:2014-09-01 19:45:48

标签: java java-8 java-time

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

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:Unable to obtain ZonedDateTime from TemporalAccessor using DateTimeFormatter and ZonedDateTime in Java 8

任何人都知道这里发生了什么?

3 个答案:

答案 0 :(得分:54)

ISO_INSTANT格式化程序已记录在案here - "这是一种特殊情况格式化程序,旨在允许人类可读的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而无需指定withZone())。

摘要

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

答案 1 :(得分:1)

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

答案 2 :(得分:0)

如果您使用的是 Java 1.8 中的org.threeten.bp,请参见速查表,以处理String to Date,反之亦然。

日期为字符串

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)