【问题标题】:DateTimeParseException when parsing String to LocalDateTime将字符串解析为 LocalDateTime 时出现 DateTimeParseException
【发布时间】:2019-02-14 20:08:05
【问题描述】:

以下工作完美:

String startDate = "2019-10-12T00:00:00.000-07:00"

LocalDateTime startDateTime = LocalDateTime.parse(startDate,
    DateTimeFormatter.ISO_ZONED_DATE_TIME);

但是,对于以下代码:

String startDate = "2019-10-12T00:00:00.000+07:00"

LocalDateTime startDateTime = LocalDateTime.parse(startDate,
    DateTimeFormatter.ISO_ZONED_DATE_TIME);

我得到一个例外:

投掷 java.time.format.DateTimeParseException:无法在索引 23 处解析文本“2019-10-12T00:00:00.000 07:00”

有人可以帮我理解这里可能有什么问题吗?

【问题讨论】:

  • 请花一些时间熟悉编辑器并正确格式化您的问题,谢谢。
  • 投票结束,无法复制。创建一个minimal reproducible example。此外,错误消息与您显示的代码不符。该消息在区域中没有+-,而是一个空格。错误消息很可能来自您未在此处显示的代码版本。 IE。消息中带有 空格 的版本,而不是 +-。这显然是错误的,因为这部分是为了告诉时间偏移,所以要么+要么-相关。
  • 我打赌2019-10-12T00:00:00.000+07:00日期字符串是一个URL查询字符串参数,+被解码为空格,因为+应该被编码为%2B。我能想到 + 被转换为空格的唯一原因。
  • @Andreas 如果您愿意,您似乎可以将其作为答案发布。

标签: java date datetime parsing java-8


【解决方案1】:

tl;博士

(A) 正如Andreas 的评论中所述,您的+ 字符在错误消息中的消失是可疑的。您可能有字符编码问题。我们这里没有足够的信息来诊断。

(B) 使用OffsetDateTime 而不是LocalDateTime 来拟合您的数据。

OffsetDateTime
.parse( "2019-10-12T00:00:00.000+07:00" )
.toInstant()
.toString()

2019-10-11T17:00:00Z

看到这个code run live at IdeOne.com

LocalDateTime 是错误的类

LocalDateTime 类缺少任何时区或与 UTC 偏移的概念。因此,根据定义,它不能代表片刻。 LocalDateTime 对象不是时间线上的一个点。

但您的输入字符串表示与 UTC 的偏移量,即2019-10-12T00:00:00.000+07:00 中的+07:00。您尝试将此值放入 LocalDateTime 是不合适的,square peg in a round hole

永远不要使用LocalDateTime 来表示特定时刻,时间轴上的特定点。此类几乎从未用于现在或过去的面向业务的应用程序中。将来进行约会时,如果您希望一天中的时间保持不变,而不管政客是否重新定义了该地区的时区,则应使用此类。

OffsetDateTime

解析为OffsetDateTime 对象。

您的输入字符串恰好完全符合文本日期时间格式的ISO 8601 标准。 java.time 类在解析/生成字符串时默认使用这些格式。所以不需要指定格式模式。

String input = "2019-10-12T00:00:00.000+07:00" ;
OffsetDateTime odt = OffsetDateTime.parse( input ) ;

UTC

要调整到UTC(零偏移)的挂钟时间,请致电OffsetDateTime::withOffsetSameInstant。为方便起见,请使用常量ZoneOffset.UTC

OffsetDateTime odtUtc = odt.withOffsetSameInstant( ZoneOffset.UTC ) ;

或者简单地在java.time中提取一个基本构建块类的对象,一个Instant。根据定义,Instant 始终采用 UTC。

Instant instant = odt.toInstant() ;

ZonedDateTime

要查看特定地区(时区)人们使用的挂钟时间查看的同一时刻,请应用ZoneId 以获取ZonedDateTime

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    【解决方案2】:

    我尝试使用 LocalDateTime 的相同解析在本地运行两个日期,它们都运行正确

    我可以通过使用不同的日期来重现您的相同错误(注意区域偏移处的 空格,而不是 +-2019-10-12T00:00:00.000 07:00 而不是 2019-10-12T00:00:00.000+07:00。这是您的错误消息显示的内容(但不是您的代码所显示的内容)。

    您确定您显示的代码版本(使用2019-10-12T00:00:00.000+07:00)是产生错误的实际版本吗?看起来你一直在使用2019-10-12T00:00:00.000 07:00,然后导致错误。

    【讨论】:

    • 我将字符串作为请求参数发送并使用邮递员对其进行测试。这可能是错误的可能原因。我应该早点提到这一点。
    • 当然,@AnkitaSingh,你是对的。您需要对字符串进行 URL 编码。
    【解决方案3】:

    startDateTime 中有一个额外的零 使用 startDateTime = "2019-10-12T00:00:00.00+07:00"

    【讨论】:

    • 毫秒是 3 位数 (HH:mm:ss.SSS) 因为 1,000 毫秒 = 1 秒
    • public static final DateTimeFormatter ISO_ZONED_DATE_TIME 类似 ISO 的日期时间格式化程序,用于格式化或解析带有偏移量和区域的日期时间,例如 '2011-12-03T10:15:30+01:00 [欧洲/巴黎]'。请参阅此文档 docs.oracle.com/javase/8/docs/api/java/time/format/…dotnetcodr.com/2015/01/11/… 它不需要 3 位数的时间
    • 提供的示例 ("10:15:30") 的格式为“HH:mm:ss”,该示例中没有毫秒。
    • 我验证了请求参数 = "2019-01-28T13:00:00.000+01:00" 邮递员将日期发送为 "2019-01-28T13:00:00.000 01:00"。这就是错误的原因。如果我使用 Swagger 运行 API,它可以正常工作。
    • DateTimeFormatter ISO_ZONED_DATE_TIME 在秒上接受从 0 到 9 位小数的任何内容。这不是问题。
    猜你喜欢
    • 1970-01-01
    • 2020-04-08
    • 2020-02-27
    • 1970-01-01
    • 1970-01-01
    • 2020-07-11
    • 2021-11-28
    • 2020-03-22
    • 1970-01-01
    相关资源
    最近更新 更多