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

时间:2011-05-17 23:02:40

标签: java datetime html5 date-parsing rfc3339

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

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

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

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

8 个答案:

答案 0 :(得分:13)

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

此处列出了individual declarations in RFC 3339的模式列表:

  • date-fullyear:yyyy
  • 日期月:MM
  • date-mday:dd
  • 时间 - 小时:HH
  • time -minute:mm
  • 时间秒:ss
  • time-secfrac:.SSSS表示毫秒 - 但不清楚如果这些数字多于或少于3位会发生什么。)
  • time-numoffset :(似乎+02:00似乎不受支持 - 相反,它支持使用+0200和{{GMT+02:00z和一些命名时区的格式1}}。)
  • 时间偏移:Z(不支持其他时区) - 在使用之前应使用'Z'。)
  • 部分时间:format.setTimezone(TimeZone.getTimeZone("UTC"))HH:mm:ss
  • 全职:HH:mm:ss.SSSHH:mm:ss'Z'
  • 完整日期:HH:mm:ss.SSS'Z'
  • 日期时间:yyyy-MM-ddyyyy-MM-dd'T'HH:mm:ss'Z'

正如我们所看到的,这似乎无法解析所有内容。也许最好从头开始实现yyyy-MM-dd'T'HH:mm:ss.SSS'Z'(为了简单起见,使用正则表达式,或者手动解析,以提高效率)。

答案 1 :(得分:13)

TL;博士

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

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

对于其他格式,请参阅各种类的parsetoString方法,例如LocalDateOffsetDateTime等。此外,搜索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及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

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

从哪里获取java.time类?

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

答案 2 :(得分:6)

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

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");
}

答案 3 :(得分:3)

Here是一种简单的方法。它可能适合您的需求。

答案 4 :(得分:3)

也许不是最优雅的方式,但肯定是我最近做的一个:

Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
cal.setTime(sdf.parse(dateInString.replace("Z", "").replace("T", "-")));

答案 5 :(得分:2)

使用您拥有的格式,例如2011-05-03T11:58:01Z,下面的代码会做。但是,我最近在Chrome和Opera中尝试了html5 datetime,它给了我2011-05-03T11:58Z - >没有下面代码无法处理的ss部分。

new Timestamp(javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar(date).toGregorianCalendar().getTimeInMillis());

答案 6 :(得分:0)

Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(datetimeInFRC3339format)

答案 7 :(得分:0)

我正在使用它:

DateTimeFormatter RFC_3339_DATE_TIME_FORMATTER = new DateTimeFormatterBuilder()
            .append(ISO_LOCAL_DATE_TIME)
            .optionalStart()
            .appendOffset("+HH:MM", "Z")
            .optionalEnd()
            .toFormatter();

示例:

String dateTimeString = "2007-05-01T15:43:26.3452+07:00";
ZonedDateTime zonedDateTime = ZonedDateTime.from(RFC_3339_DATE_TIME_FORMATTER.parse(dateTimeString));