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

Ada*_*dam 25 java html5 datetime rfc3339 date-parsing

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

我想将其解析为Java Date或Calendar Object.

理想情况下,解决方案应该包含以下内容:

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

Bas*_*que 16

TL;博士

Instant.parse( "2011-05-03T11:58:01Z" )
Run Code Online (Sandbox Code Playgroud)

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并使用更精细的纳秒分辨率而不是毫秒.

Instant instant = Instant.parse( "2011-05-03T11:58:01Z" ) ;
Run Code Online (Sandbox Code Playgroud)

ZonedDateTime(不是Calendar)

要通过特定地区(时区)的人们所使用的挂钟时间来查看同一时刻,请应用a 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.
Run Code Online (Sandbox Code Playgroud)

转换

我强烈建议尽可能避免使用旧版日期时间类.但是,如果您必须与尚未更新到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.
Run Code Online (Sandbox Code Playgroud)

ZonedDateTime替换GregorianCalendar.

java.util.GregorianCalendar myGregCal = java.util.GregorianCalendar.from( zdt ) ;  // From modern to legacy.
ZonedDateTime zdt = myGregCal.toZonedDateTime() ;           // From legacy to modern.
Run Code Online (Sandbox Code Playgroud)

如果你有一个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.
Run Code Online (Sandbox Code Playgroud)

项目符号问题

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

  • 没有外部库(罐子)

java.time类Java中内嵌的8,9,10,和更高版本.稍后的Android中还包含一个实现.对于早期的Java和早期的Android,请参阅本答案的下一部分.

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

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

对于其它的格式,见parsetoString各种类的方法,例如LocalDate,OffsetDateTime等.此外,搜索Stack Overflow,因为有很多关于此主题的示例和讨论.

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

要验证输入字符串,请捕获DateTimeParseException.

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

关于java.time

java.time框架是建立在Java 8和更高版本.这些类取代麻烦的老传统日期时间类,如java.util.Date,Calendar,和SimpleDateFormat.

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

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

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

从哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time.该项目是未来可能添加到java.time的试验场.您可以在此比如找到一些有用的类Interval,YearWeek,YearQuarter,和更多.

  • 不幸的是,“Instant”在时区“2019-11-30T10:30:00+02:00”或“2019-10-12T07:20:50.52+00:00”的日期上总是失败,无论有毫秒还是没有毫秒。它失败并出现诸如“java.time.format.DateTimeParseException:文本'2019-11-30T10:30:00 + 02:00'无法在索引19处解析”之类的错误,或在“索引23”上相同。因此我不能在生产中使用它,它实际上不支持 RFC-3339。 (3认同)
  • @DmitriyPopov `OffsetDateTime.parse( "2019-11-30T10:30:00+02:00" )` 和 `OffsetDateTime.parse( "2019-10-12T07:20:50.52+00:00" )`。默认情况下,所有 *java.time* 类都使用 ISO 8601 格式来解析/生成文本。您只需为文本语义选择适当的 *java.time* 类。 (2认同)

Paŭ*_*ann 13

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

这里是RFC 3339中各个声明的模式列表:

  • 日期fullyear: yyyy
  • 日期,月份: MM
  • 日期MDAY: dd
  • 时间小时: HH
  • 时间 - 分钟: mm
  • 时间第二的: ss
  • 时间secfrac: .SSS (S毫秒表示,虽然-目前尚不清楚是否有更多或者这些不超过3位会发生什么.)
  • time-numoffset :( +02:00好像不支持 - 相反它支持格式+0200,GMT+02:00一些命名时区使用zZ.)
  • 时间偏移:( '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从头开始实现一个更好的主意(使用正则表达式,为简单起见,或者手工解析,以提高效率).

  • SimpleDateFormat会阻塞超出millis的分数. (3认同)

vic*_*ite 6

刚刚发现Google在Google HTTP Client Library中实现了Rfc3339解析器

https://github.com/google/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");
}
Run Code Online (Sandbox Code Playgroud)

  • 您似乎正在使用* java.time *类(很好)。但是在* java.time *中没有`DateTime`类。我很困惑。 (2认同)