无法在 Java 8 中使用 DateTimeFormatter 和 ZonedDateTime 从 TemporalAccessor 获取 ZonedDateTime

Unable to obtain ZonedDateTime from TemporalAccessor using DateTimeFormatter and ZonedDateTime in Java 8

提问人:Faliorn 提问时间:5/12/2014 最后编辑:Anton MenshovFaliorn 更新时间:7/21/2021 访问量:73680

问:

我最近迁移到了 Java 8,希望能够更轻松地处理本地和分区时间。

但是,在我看来,在解析一个简单的日期时,我面临着一个简单的问题。

public static ZonedDateTime convertirAFecha(String fecha) throws Exception {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
            ConstantesFechas.FORMATO_DIA).withZone(
            obtenerZonaHorariaServidor());

    ZonedDateTime resultado = ZonedDateTime.parse(fecha, formatter);
    return resultado;
}

就我而言:

  • FECHA 是“15/06/2014”
  • ConstantesFechas.FORMATO_DIA是“dd/MM/yyyy”
  • obtenerZonaHorariaServidor 返回 ZoneId.systemDefault()

所以,这是一个简单的例子。但是,分析会引发以下异常:

java.time.format.DateTimeParseException:文本“15/06/2014”无法 被解析:无法从 TemporalAccessor 获取 ZonedDateTime: {},ISO 解析为 2014-06-15 java.time.format.Parsed 类型

有什么提示吗?我一直在尝试解析和使用 TemporalAccesor 的不同组合,但到目前为止没有任何运气。

时区 日期时间格式 java-time

评论

1赞 nsandersen 12/5/2018
这些确实是 fechas :) !
1赞 Ondra Žižka 6/9/2019
我在这里详细讨论了背后的理论:stackoverflow.com/a/56508200/145989java.time

答:

77赞 assylias 5/12/2014 #1

这不起作用,因为您的输入(和格式化程序)没有时区信息。一个简单的方法是将你的日期解析为第一个(没有时间或时区信息),然后创建一个:LocalDateZonedDateTime

public static ZonedDateTime convertirAFecha(String fecha) {
  DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
  LocalDate date = LocalDate.parse(fecha, formatter);

  ZonedDateTime resultado = date.atStartOfDay(ZoneId.systemDefault());
  return resultado;
}

评论

2赞 sleske 4/15/2020
是的,出现此问题的原因是格式化程序未提供时区。我编辑了你的答案来强调这一点。你是对的:-)。
6赞 sleske 4/15/2020
顺便说一句,除了使用 LocalDate 之外,另一个解决方案是使用 DateTimeFormatter.withZone(x) 为格式化程序设置默认时区。然后,您可以使用 ZonedDateTime,它将始终具有时区 x。
18赞 Meno Hochschild 5/12/2014 #2

这是一个错误,请参阅 JDK-bug-log。根据这些信息,Java 9 和 Java 8u20 的问题得到了解决。尝试下载最新的 Java 8 版本。今天 2014-05-12: 抢先体验版 8u20 可用。

更新:

我个人认为,既然你只有并期望“dd/MM/yyyy”作为模式,你应该使用@assylias已经提出的主要类型。关于您的上下文,几乎可以肯定使用 .您希望如何处理此类型的对象?我只能将专门的时区计算视为用例。而且你甚至不能直接将这些对象存储在数据库中,所以这种类型远没有许多人认为的那么有用。LocalDateZonedDateTimeZonedDateTime

我所描述的用例问题确实是 Java-8 与旧的 -class(一种多合一类型)相比引入的一个新方面。用户必须开始考虑为他们的问题和用例选择适当的时态类型。GregorianCalendar

评论

2赞 assylias 5/12/2014
不错的收获 - 尽管我相信操作仍然需要提供一个时间才能将输入解析为 ZonedDateTime。
1赞 Meno Hochschild 5/12/2014
@assylias是的,OP 应该至少在时间部分扩展模式或选择您的建议。这也可能取决于宽松的解析、配置等。实际上,我无法在当前位置使用 Java-8 测试这些细节。
1赞 Halil 5/15/2019
我将 ZonedDateTime 用于“dd/MM/yyyy”日期。这解决了我的问题since you only have and expect "dd/MM/yyyy" as pattern you should use LocalDate
0赞 Meno Hochschild 4/15/2020
@sleske 你说:“......OP 的代码不使用 withZone...”但是 OP 提供的代码确实使用了 ,见上文。因此,提到的 JDK-bug 仍然与那些早期 JDK 版本中没有真正设置区域的问题相匹配。无论如何,其他大问题是:使用代替和缺少时钟时间模式部分(如果坚持)。withZone(…)ZonedDateTime.parse(…)LocalDate.parse(…)ZonedDateTime.parse(…)
0赞 sleske 4/15/2020
哎呀,对不起,误读了问题......我的错误。我删除了我的评论。
14赞 Sergey Ponomarev 12/4/2014 #3

简单来说,这条线

ZonedDateTime.parse('2014-04-23', DateTimeFormatter.ISO_OFFSET_DATE_TIME)

抛出异常:

Text '2014-04-23' could not be parsed at index 10
java.time.format.DateTimeParseException: Text '2014-04-23' could not be parsed at index 10

这对我来说看起来像一个错误。

我使用了这个解决方法:

String dateAsStr = '2014-04-23';
if (dateAsStr.length() == 10) {
    dateAsStr += 'T00:00:00';
}
ZonedDateTime.parse(dateAsStr, DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.systemDefault()));
2赞 Dahar Youssef 10/27/2015 #4

只是一个转换示例,我相信有些人会在下面遇到例外

(java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: 2014-10-24T18:22:09.800Z of type java.time.Instant)

如果他们尝试

LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant());

要解决此问题,请传入区域 -

LocalDateTime localDateTime = LocalDateTime.from(new Date()
        .toInstant().atZone(ZoneId.of("UTC")));
8赞 fIwJlxSzApHEZIl 3/2/2018 #5

如果来自 Google:

而不是做:

ZonedDateTime.from(new Date().toInstant());

试试这个:

ZonedDateTime.ofInstant(new Date(), ZoneId.of("UTC"));