如何使用 Java 解析 RFC 3339 日期时间?tl;博士国际标准化组织 ISO 8601关于 java.time

2022-09-01 05:54:25

我正在尝试解析从HTML5输入字段中返回为值的日期。在 Opera 中试用它,看看一个示例。返回的日期如下所示:。datetime2011-05-03T11:58:01Z

我想将其解析为Java日期或日历对象。

理想情况下,解决方案应具有以下内容:

  • 无外部库(罐)
  • 处理所有可接受的 RFC 3339 格式
  • 字符串应该能够轻松验证以查看它是否是有效的 RFC 3339 日期

答案 1

tl;博士

Instant.parse( "2011-05-03T11:58:01Z" )

国际标准化组织 ISO 8601

实际上,RFC 3339只是实际标准ISO 8601的自我声明的“配置文件”。

RFC的不同之处在于,它故意违反ISO 8601,允许零小时()的负偏移量,并赋予其“偏移未知”的语义含义。这种语义对我来说似乎是一个非常糟糕的主意。我建议坚持更明智的ISO 8601规则。在ISO 8601中,完全没有偏移量意味着偏移量是未知的 - 这是一个明显的含义,而RFC规则是晦涩难懂的。-00:00

现代 java.time 类在解析/生成字符串时默认使用 ISO 8601 格式。

输入字符串表示 UTC 中的某个时刻。末尾是 UTC 的缩写,表示 UTC。ZZulu

Instant(不是Date)

现代类表示 UTC 中的某个时刻。此类替换 java.util.Date,并使用纳秒而不是毫秒的更精细分辨率。Instant

Instant instant = Instant.parse( "2011-05-03T11:58:01Z" ) ;

ZonedDateTime(不是Calendar)

要通过某个地区(时区)的人们使用的挂钟时间查看相同的时刻,请应用 a 以获取 .此类将替换 java.util.Calendar 类。ZoneIdZonedDateTimeZonedDateTime

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, same point on the timeline, different wall-clock time.

转换

我强烈建议尽可能避免使用旧版日期时间类。但是,如果您必须与尚未更新到java.time的旧代码进行互操作,则可以来回转换。调用添加到类的新方法。

Instant取代。java.util.Date

java.util.Date myJUDate = java.util.Date.from( instant ) ;  // From modern to legacy.
Instant instant = myJUDate.toInstant() ;                    // From legacy to modern.

ZonedDateTime取代。GregorianCalendar

java.util.GregorianCalendar myGregCal = java.util.GregorianCalendar.from( zdt ) ;  // From modern to legacy.
ZonedDateTime zdt = myGregCal.toZonedDateTime() ;           // From legacy to modern.

如果你有一个,那实际上是一个,cast。java.util.CalendarGregorianCalendar

java.util.GregorianCalendar myGregCal = ( java.util.GregorianCalendar ) myCal ;  // Cast to the concrete class.
ZonedDateTime zdt = myGregCal.toZonedDateTime() ;           // From legacy to modern.

项目符号关注

关于您问题的具体问题...

  • 无外部库(罐)

java.time 类内置于 Java 8、9、10 及更高版本中。一个实现也包含在后来的Android中。对于早期的Java和早期的Android,请参阅本答案的下一节。

  • 处理所有可接受的 RFC 3339 格式

各种java.time类处理我所知道的每一种ISO 8601格式。他们甚至处理一些神秘地从后来的标准版本中消失的格式。

有关其他格式,请参阅各种类(如 、等)的 和 方法。另外,搜索堆栈溢出,因为有很多关于这个主题的示例和讨论。parsetoStringLocalDateOffsetDateTime

  • 字符串应该能够轻松验证以查看它是否是有效的 RFC 3339 日期

若要验证输入字符串,请为 DateTimeParseException 陷印

try {
    Instant instant = Instant.parse( "2011-05-03T11:58:01Z" ) ;
} catch ( DateTimeParseException e ) {
    … handle invalid input
}

关于 java.time

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

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

要了解更多信息,请参阅 Oracle 教程。搜索 Stack Overflow 以获取许多示例和解释。规格是JSR 310

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

从哪里获取 java.time 类?

ThreeTen-Extra 项目通过其他类扩展了 java.time。这个项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuarter


答案 2

因此,原则上这将使用不同的SimpleDateFormat模式来完成。

下面是 RFC 3339 中各个声明的模式列表:

  • date-fullear:yyyy
  • 日期-月份:MM
  • 日期-mday:dd
  • 时间-小时:HH
  • 时间-分钟:mm
  • 时间秒:ss
  • time-secfrac:( 表示毫秒,但是 - 目前尚不清楚如果这些数字的多于或少于3位,会发生什么。.SSSS
  • time-numoffset:(好像似乎不受支持 - 相反,它支持格式,以及一些使用 和 的命名时区。+02:00+0200GMT+02:00zZ
  • 时间偏移量:(不支持其他时区) - 您应该在使用之前使用。'Z'format.setTimezone(TimeZone.getTimeZone("UTC"))
  • 部分时间:或 .HH:mm:ssHH: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