如何使用 Java 解析 RFC 3339 日期时间?
我正在尝试解析从 HTML5 datetime
输入字段返回的日期。在 Opera 中尝试一下以查看示例。返回的日期如下所示:2011-05-03T11:58:01Z
。
我想将其解析为 Java 日期或日历对象。
理想情况下,解决方案应具有以下内容:
- 没有外部库(jar)
- 处理所有可接受的 RFC 3339 格式
- 应该能够轻松验证字符串以查看它是否是有效的 RFC 3339 日期
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
tl;dr
ISO 8601
实际上,RFC 3339 只是一个自称的“配置文件”实际标准 ISO 8601。
RFC 的不同之处在于,它故意违反 ISO 8601,允许零小时 (
-00:00
) 的负偏移,并赋予其“偏移未知”的语义含义。对我来说,这种语义似乎是一个非常糟糕的主意。我建议坚持更明智的 ISO 8601 规则。在 ISO 8601 中,根本没有偏移量意味着偏移量未知——这是一个显而易见的含义,而 RFC 规则则很深奥。现代 java.time 类在解析/生成字符串时默认使用 ISO 8601 格式。
您的输入字符串代表 UTC 中的某个时刻。末尾的
Z
是Zulu
的缩写,表示 UTC。Instant
(不是Date
)现代类
Instant
表示UTC 中的某个时刻。此类替换java.util.Date
,并使用纳秒而不是毫秒的更精细分辨率。
ZonedDateTime
(不是Calendar
)要通过特定区域(时区)的人们使用的挂钟时间查看同一时刻,请应用
ZoneId
获取ZonedDateTime
。此类ZonedDateTime
替换 <代码>java.util.Calendar类。转换
我强烈建议尽可能避免使用遗留的日期时间类。但是,如果您必须与尚未更新到 java.time 的旧代码进行互操作,则可以来回转换。调用添加到旧类中的新方法。
Instant
取代了java.util.Date
。ZonedDateTime
取代GregorianCalendar
。如果您的
java.util.Calendar
实际上是GregorianCalendar
,请进行强制转换。项目符号关注点
关于您的问题的具体问题...
java.time 类内置于 Java 8、9、10 及更高版本中。后来的 Android 中也包含了一个实现。对于早期的 Java 和早期的 Android,请参阅本答案的下一部分。
各种 java.time 类可以处理我所知道的每种 ISO 8601 格式。他们甚至处理一些从标准的后续版本中神秘消失的格式。
对于其他格式,请参阅各种类的
parse
和toString
方法,例如LocalDate
、OffsetDateTime
等在。另外,请搜索 Stack Overflow,因为有许多关于此主题的示例和讨论。要验证输入字符串,请捕获
DateTimeParseException
。关于 java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧遗留日期时间类,例如
java.util.Date
,日历
,&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
,以及更多。tl;dr
ISO 8601
Actually, RFC 3339 is but a mere self-proclaimed “profile” of the actual standard, ISO 8601.
The RFC is different in that it purposely violates ISO 8601 to allow a negative offset of zero hours (
-00:00
) and gives that a semantic meaning of “offset unknown“. That semantic seems like a very bad idea to me. I advise sticking with the more sensible ISO 8601 rules. In ISO 8601, having no offset at all means the offset is unknown – an obvious meaning, whereas the RFC rule is abstruse.The modern java.time classes use the ISO 8601 formats by default when parsing/generating strings.
Your input string represents a moment in UTC. The
Z
on the end is short forZulu
and means UTC.Instant
(notDate
)The modern class
Instant
represents a moment in UTC. This class replacesjava.util.Date
, and uses a finer resolution of nanoseconds rather than milliseconds.ZonedDateTime
(notCalendar
)To see that same moment through the wall-clock time used by the people of a certain region (a time zone), apply a
ZoneId
to get aZonedDateTime
. This classZonedDateTime
replaces thejava.util.Calendar
class.Converting
I strongly recommend avoiding the legacy date-time classes when possible. But if you must inter-operate with old code not yet updated to java.time, you may convert back-and-forth. Call new methods added to the old classes.
Instant
replacesjava.util.Date
.ZonedDateTime
replacesGregorianCalendar
.If you have a
java.util.Calendar
that is actually aGregorianCalendar
, cast.Bulleted concerns
Regarding your Question’s specific issues…
The java.time classes are built into Java 8, 9, 10, and later. An implementation is also included in later Android. For earlier Java and earlier Android, see the next section of this Answer.
The various java.time classes handle every ISO 8601 format I know of. They even handle some formats that mysteriously disappeared from later editions of the standard.
For other formats, see the
parse
andtoString
methods of the various classes such asLocalDate
,OffsetDateTime
, and so on. Also, search Stack Overflow as there are many examples and discussions on this topic.To validate input strings, trap for
DateTimeParseException
.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.因此,原则上这将使用不同的 SimpleDateFormat 模式。
以下是 RFC 3339 中的单独声明的模式列表:
yyyy
MM
dd
HH
mm
ss
.SSS
(S
表示毫秒,但它是不清楚如果这些数字多于或少于 3 位会发生什么。)+02:00
似乎不受支持 - 相反,它支持格式+ 0200
、GMT+02:00
以及一些使用z
和Z
的命名时区。)'Z'
(不支持其他时区) - 在使用此功能之前,您应该使用format.setTimezone(TimeZone.getTimeZone("UTC"))
。)HH:mm:ss
或HH:mm:ss.SSS
。HH:mm:ss'Z'
或HH:mm:ss.SSS'Z'
。yyyy-MM-dd
yyyy-MM-dd'T'HH:mm:ss'Z'
或yyyy-MM -dd'T'HH:mm:ss.SSS'Z'
正如我们所看到的,这似乎无法解析所有内容。也许从头开始实现 RFC3339DateFormat 是一个更好的主意(为了简单起见,使用正则表达式,为了提高效率,使用手动解析)。
So, in principle this would be done using different SimpleDateFormat patterns.
Here a list of patterns for the individual declarations in RFC 3339:
yyyy
MM
dd
HH
mm
ss
.SSS
(S
means millisecond, though - it is not clear what would happen if there are more or less than 3 digits of these.)+02:00
seems to be not supported - instead it supports the formats+0200
,GMT+02:00
and some named time zones usingz
andZ
.)'Z'
(not supporting other time zones) - you should useformat.setTimezone(TimeZone.getTimeZone("UTC"))
before using this.)HH:mm:ss
orHH:mm:ss.SSS
.HH:mm:ss'Z'
orHH:mm:ss.SSS'Z'
.yyyy-MM-dd
yyyy-MM-dd'T'HH:mm:ss'Z'
oryyyy-MM-dd'T'HH:mm:ss.SSS'Z'
As we can see, this seems not to be able to parse everything. Maybe it would be a better idea to implement an
RFC3339DateFormat
from scratch (using regular expressions, for simplicity, or parsing by hand, for efficiency).刚刚发现谷歌在谷歌HTTP客户端库中实现了Rfc3339解析器
https://github.com/google/google-http-java-client/blob/dev/google-http-client/src /main/java/com/google/api/client/util/DateTime.java
已测试。它可以很好地解析不同的亚秒时间片段。
Just found that google implemented Rfc3339 parser in Google HTTP Client Library
https://github.com/google/google-http-java-client/blob/dev/google-http-client/src/main/java/com/google/api/client/util/DateTime.java
Tested. It works well to parse varies sub seconds time fragment.
使用您的格式,例如 2011-05-03T11:58:01Z,下面的代码就可以了。然而,我最近在 Chrome 和 Opera 中尝试了 html5 datetime,它给了我 2011-05-03T11:58Z -->没有无法通过下面的代码处理的 ss 部分。
With the format you have e.g. 2011-05-03T11:58:01Z, below code will do. However, I recently tryout html5 datetime in Chrome and Opera, it give me 2011-05-03T11:58Z --> do not have the ss part which cannot be handled by code below.
也许不是最优雅的方式,但肯定是我最近制作的一种方式:
Maybe not the most elegant way, but certainly working one I recently made:
我正在使用这个:
示例:
I'm using this:
Example:
虽然,这个问题很老了,但它可能会帮助那些想要这个答案的 Kotlin 版本的人。通过使用此文件,任何人都可以将 Rfc3339 日期转换为任何日期格式。在这里,我采用一个空文件名
DateUtil
并创建一个名为getDateString()
的函数,该函数有 3 个参数。DateUtil.kt
现在在您的 Activity/fuction/dataSourse Mapper 中使用此方法来获取字符串格式的日期,如下所示
,输出将如下所示
Though, The question is very old, but it may help one who wants it Kotlin version of this answer. By using this file, anyone can convert a Rfc3339 date to any date-format. Here I take a empty file name
DateUtil
and create a function calledgetDateString()
which has 3 arguments.DateUtil.kt
And now use this method in your activity/fuction/dataSourse Mapper to get Date in String format like this
and output will be like this
为了将来参考,作为替代方案,您可以使用 ITU[1],它是手写的,可以精确处理 RFC-3339 解析,并且还可以让您轻松处理闰秒。该库没有依赖关系,大小仅为 18 kB。
完全披露:我是作者
[1] - https://github.com/ethlo/itu
For future reference, as an alternative, you could use ITU[1] which is hand-written to deal with exactly RFC-3339 parsing and also lets you easily deal with leap seconds. The library is dependency-free and only weighs in at 18 kB.
Full disclosure: I'm the author
[1] - https://github.com/ethlo/itu