本文介绍了无法将 ISO 8601 格式的字符串解析为 Java 8 日期,在偏移量中缺少冒号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对 java 8 日期格式/解析功能有点沮丧.我试图找到 Jackson 配置和 DateTimeFormatter"2018-02-13T10:20:12.120+0000" 字符串解析为任何 Java 8 日期,但没有找到它.
这是运行正常的 java.util.Date 示例:

Date date = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSZZZ").parse("2018-02-13T10:20:12.120+0000");

相同的格式不适用于新的日期时间 api

ZonedDateTime dateTime = ZonedDateTime.parse("2018-02-13T10:20:12.120+0000",DateTimeFormatter.ofPattern("yyyy-MM-dd'T'hh:mm:ss.SSSZZZ"));

我们应该能够以任何适合 FE UI 应用程序的格式来格式化/解析日期.也许我误解或弄错了一些东西,但我认为 java.util.Date 提供了更多的格式灵活性和更易于使用.

解决方案

tl;dr

直到修复错误:

OffsetDateTime.parse("2018-02-13T10:20:12.120+0000" ,DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX"))

修复错误时:

OffsetDateTime.parse( "2018-02-13T10:20:12.120+0000" )

详情

您使用了错误的类.

避免使用麻烦的旧遗留类,例如 DateCalendarSimpleDateFormat.现在被 java.time 取代类.

ZonedDateTime 类很好,它是 java.time 的一部分.但它适用于全时区.您的输入字符串只有一个 offset-from-UTC.相比之下,完整时区是在过去、现在和未来的不同时间点对一个区域有效的偏移量的集合.例如,在北美大部分地区的夏令时 (DST) 中,偏移量每年变化两次,当我们将时钟向前移动一个小时时,偏移量在春季变小,而当我们将时钟向后移动一个小时时,偏移量会在秋季恢复到更长的值.小时.

OffsetDateTime

对于仅偏移量而不是时区,请使用 OffsetDateTime 类.

您的输入字符串符合 ISO 8601 标准.java.time 类在解析/生成字符串时默认使用标准格式.所以不需要指定格式模式.

OffsetDateTime odt = OffsetDateTime.parse("2018-02-13T10:20:12.120+0000");

嗯,那应该奏效了.不幸的是,Java 8 中存在一个错误(至少到 Java 8 Update 121),该类无法解析忽略小时和分钟之间冒号的偏移量.所以这个 bug 咬在 +0000 而不是 +00:00.因此,在修复到来之前,您可以选择两种解决方法:(a) hack,操作输入字符串,或 (b) 定义显式格式模式.

技巧:操作输入字符串以插入冒号.

String input = "2018-02-13T10:20:12.120+0000".replace( "+0000" , "+00:00" );OffsetDateTime odt = OffsetDateTime.parse( input );

DateTimeFormatter

更可靠的解决方法是在 DateTimeFormatter 对象中定义和传递格式模式.

String input = "2018-02-13T10:20:12.120+0000" ;DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX");OffsetDateTime odt = OffsetDateTime.parse( input , f );

odt.toString(): 2018-02-13T10:20:12.120Z

顺便说一句,这里有一个提示:我发现,对于许多协议和库,如果偏移量总是带有冒号,总是有小时和分钟(即使分钟为零),并且总是使用填充零(-05:00 而不是 -5).

DateTimeFormatterBuilder

对于更灵活的格式化程序,通过 DateTimeFormatterBuilder,参见 这个优秀的答案一个重复的问题.

即时

如果您想使用始终采用 UTC 的值(您应该这样做),请提取一个 Instant 对象.

Instant Instant = odt.toInstant();

ZonedDateTime

如果您想通过某个地区的挂钟时间来查看那一刻,应用时区.

ZoneId z = ZoneId.of("美国/蒙特利尔");ZonedDateTime zdt = odt.atZoneSameInstant(z);

查看此代码在 IdeOne.com 上实时运行.

所有这些在许多问题的许多答案中已经多次提及.请在发帖前彻底搜索 Stack Overflow.您会发现数十个(如果不是数百个)示例.

关于java.time

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

Joda-Time 项目,现在在 维护模式,建议迁移到 java.time 类.

要了解更多信息,请参阅 Oracle 教程.并在 Stack Overflow 上搜索许多示例和解释.规范是 JSR 310.

您可以直接与您的数据库交换 java.time 对象.使用符合 JDBC 驱动程序jeps/170" rel="noreferrer">JDBC 4.2 或更高版本.不需要字符串,不需要 java.sql.* 类.

从哪里获取 java.time 类?

ThreeTen-Extra 项目扩展了 java.time额外的课程.该项目是未来可能添加到 java.time 的试验场.您可能会在这里找到一些有用的类,例如 Interval, YearWeek, YearQuarter更多.

I'm a little bit frustrated of java 8 date format/parse functionality. I was trying to find Jackson configuration and DateTimeFormatter to parse "2018-02-13T10:20:12.120+0000" string to any Java 8 date, and didn't find it.
This is java.util.Date example which works fine:

Date date = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSZZZ")
                      .parse("2018-02-13T10:20:12.120+0000");

The same format doesn't work with new date time api

ZonedDateTime dateTime = ZonedDateTime.parse("2018-02-13T10:20:12.120+0000",
                   DateTimeFormatter.ofPattern("yyyy-MM-dd'T'hh:mm:ss.SSSZZZ"));

We should be able to format/parse date in any format suitable for FE UI application. Maybe I misunderstand or mistake something, but I think java.util.Date gives more format flexibility and easier to use.

解决方案

tl;dr

Until bug is fixed:

OffsetDateTime.parse(
    "2018-02-13T10:20:12.120+0000" ,
    DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" )
)

When bug is fixed:

OffsetDateTime.parse( "2018-02-13T10:20:12.120+0000" )

Details

You are using the wrong classes.

Avoid the troublesome old legacy classes such as Date, Calendar, and SimpleDateFormat. Now supplanted by the java.time classes.

The ZonedDateTime class you used is good, it is part of java.time. But it is intended for a full time zone. Your input string has merely an offset-from-UTC. A full time zone, in contrast, is a collection of offsets in effect for a region at different points in time, past, present, and future. For example, with Daylight Saving Time (DST) in most of North America, the offsets change twice a year growing smaller in the Spring as we shift clocks forward an hour, and restoring to a longer value in the Autumn when we shift clocks back an hour.

OffsetDateTime

For only an offset rather than a time zone, use the OffsetDateTime class.

Your input string complies with the ISO 8601 standard. The java.time classes use the standard formats by default when parsing/generating strings. So no need to specify a formatting pattern.

OffsetDateTime odt = OffsetDateTime.parse( "2018-02-13T10:20:12.120+0000" );

Well, that should have worked. Unfortunately, there is a bug in Java 8 (at least up through Java 8 Update 121) where that class fails to parse an offset omitting the colon between hours and minutes. So the bug bites on +0000 but not +00:00. So until a fix arrives, you have a choice of two workarounds: (a) a hack, manipulating the input string, or (b) define an explicit formatting pattern.

The hack: Manipulate the input string to insert the colon.

String input = "2018-02-13T10:20:12.120+0000".replace( "+0000" , "+00:00" );
OffsetDateTime odt = OffsetDateTime.parse( input );

DateTimeFormatter

The more robust workaround is to define and pass a formatting pattern in a DateTimeFormatter object.

String input = "2018-02-13T10:20:12.120+0000" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" );
OffsetDateTime odt = OffsetDateTime.parse( input , f );

By the way, here is a tip: I have found that with many protocols and libraries, your life is easier if your offsets always have the colon, always have both hours and minutes (even if minutes are zero), and always use a padding zero (-05:00 rather than -5).

DateTimeFormatterBuilder

For a more flexible formatter, created via DateTimeFormatterBuilder, see this excellent Answer on a duplicate Question.

Instant

If you want to work with values that are always in UTC (and you should), extract an Instant object.

Instant instant = odt.toInstant();

ZonedDateTime

If you want to view that moment through the lens of some region’s wall-clock time, apply a time zone.

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = odt.atZoneSameInstant( z );

See this code run live at IdeOne.com.

All of this has been covered many times in many Answers for many Questions. Please search Stack Overflow thoroughly before posting. You would have discovered many dozens, if not hundreds, of examples.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

这篇关于无法将 ISO 8601 格式的字符串解析为 Java 8 日期,在偏移量中缺少冒号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-05 05:04