如何使用 Java 解析 RFC 3339 日期时间?

新手上路,请多包涵

我正在尝试解析作为值从 HTML5 datetime 输入字段返回的日期。在 Opera 中尝试查看示例。返回的日期如下所示: 2011-05-03T11:58:01Z

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

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

  • 没有外部库(罐子)
  • 处理所有可接受的 RFC 3339 格式
  • 一个字符串应该能够很容易地被验证以查看它是否是一个有效的 RFC 3339 日期

原文由 Adam 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.7k
2 个回答

刚刚发现google在Google HTTP Client Library中实现了Rfc3339 parser

https://github.com/google/google-http-java-client/blob/dev/google-http-client/src/main/java/com/google/api/client/util/DateTime.java

经过测试。它可以很好地解析不同的亚秒级时间片段。

 import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

import com.google.api.client.util.DateTime;

DateTimeFormatter formatter = DateTimeFormatter
            .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
            .withZone(ZoneId.of("UTC"));

@Test
public void test1e9Parse() {
    String timeStr = "2018-04-03T11:32:26.553955473Z";

    DateTime dateTime = DateTime.parseRfc3339(timeStr);
    long millis = dateTime.getValue();

    String result = formatter.format(new Date(millis).toInstant());

    assert result.equals("2018-04-03T11:32:26.553Z");
}

@Test
public void test1e3Parse() {
    String timeStr = "2018-04-03T11:32:26.553Z";

    DateTime dateTime = DateTime.parseRfc3339(timeStr);
    long millis = dateTime.getValue();

    String result = formatter.format(new Date(millis).toInstant());

    assert result.equals("2018-04-03T11:32:26.553Z");
}

@Test
public void testEpochSecondsParse() {

    String timeStr = "2018-04-03T11:32:26Z";

    DateTime dateTime = DateTime.parseRfc3339(timeStr);
    long millis = dateTime.getValue();

    String result = formatter.format(new Date(millis).toInstant());

    assert result.equals("2018-04-03T11:32:26.000Z");
}

原文由 vicknite 发布,翻译遵循 CC BY-SA 3.0 许可协议

tl;博士

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

国际标准化组织 8601

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

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

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

您的输入字符串代表 UTC 中的一个时刻。最后的 ZZulu 的缩写,表示 UTC。

Instant (不是 Date

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

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

ZonedDateTime (不是 Calendar

要通过某个地区(时区)的人们使用的挂钟时间查看同一时刻,请应用 ZoneId 以获得 ZonedDateTime 。此类 ZonedDateTime 替换了 java.util.Calendar 类。

 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.

如果你有一个 java.util.Calendar 实际上是一个 GregorianCalendar ,演员。

 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 格式。他们甚至处理了一些从标准的后续版本中神秘消失的格式。

For other formats, see the parse and toString methods of the various classes such as LocalDate , OffsetDateTime , and so on.此外,搜索 Stack Overflow,因为有 很多 关于此主题的示例和讨论。

  • 一个字符串应该能够很容易地被验证以查看它是否是一个有效的 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 类?

  • Java SE 8Java SE 9 和更高版本
  • 内置。
  • 带有捆绑实现的标准 Java API 的一部分。
  • Java 9 添加了一些次要功能和修复。
  • Java SE 6Java SE 7
  • ThreeTen -Backport 中的大部分 java.time 功能都反向移植到 Java 6 和 7。
  • 安卓
  • 更高版本的 Android 捆绑了 java.time 类的实现。
  • 对于早期的 Android(<26), ThreeTenABP 项目采用了 ThreeTen-Backport (如上所述)。请参阅 如何使用 ThreeTenABP…。

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

原文由 Basil Bourque 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题