93

我正在尝试解析如下所示的日期:

2010-04-05T17:16:00Z

这是每个http://www.ietf.org/rfc/rfc3339.txt的有效日期。'Z' 字面量(引号)“暗示 UTC 是指定时间的首选参考点。

如果我尝试使用 SimpleDateFormat 和这种模式来解析它:

yyyy-MM-dd'T'HH:mm:ss

它将被解析为 Mon Apr 05 17:16:00 EDT 2010


SimpleDateFormat无法使用以下模式解析字符串:

yyyy-MM-dd'T'HH:mm:ssz
yyyy-MM-dd'T'HH:mm:ssZ

我可以明确设置TimeZone以在 上使用SimpleDateFormat以获得预期的输出,但我认为这没有必要。有什么我想念的吗?是否有替代日期解析器?

4

12 回答 12

63

Java 不能正确解析 ISO 日期。

类似于麦肯齐的回答。

只需修复Z解析之前的问题。

代码

String string = "2013-03-05T18:05:05.000Z";
String defaultTimezone = TimeZone.getDefault().getID();
Date date = (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")).parse(string.replaceAll("Z$", "+0000"));

System.out.println("string: " + string);
System.out.println("defaultTimezone: " + defaultTimezone);
System.out.println("date: " + (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")).format(date));

结果

string: 2013-03-05T18:05:05.000Z
defaultTimezone: America/New_York
date: 2013-03-05T13:05:05.000-0500
于 2013-03-05T18:40:23.707 回答
59

您正在解析的日期采用ISO 8601格式。

在 Java 7 中,读取和应用时区后缀的模式应为yyyy-MM-dd'T'HH:mm:ssX

于 2014-06-24T18:46:52.197 回答
38

tl;博士

Instant.parse ( "2010-04-05T17:16:00Z" )

ISO 8601 标准

您的字符串符合ISO 8601标准(其中提到的RFC 3339是一个配置文件)。

避免使用 java.util.Date

与 Java 捆绑在一起的 java.util.Date 和 .Calendar 类是出了名的麻烦。避开他们。

而是使用Joda-Time库或 Java 8 中的新 java.time 包。两者都使用 ISO 8601 作为它们解析和生成日期时间值的字符串表示的默认值。

java.time

Java 8 和更高版本中内置的java.time框架取代了麻烦的旧 java.util.Date/.Calendar 类。新课程的灵感来自非常成功的Joda-Time框架,旨在作为其继任者,在概念上相似但重新架构。由JSR 310定义。由ThreeTen-Extra项目扩展。请参阅教程

java.time 中的类代表UTC时区Instant时间线上的时刻。

Z输入字符串末尾的Zulu代表UTC. 这样的字符串可以直接被Instant类解析,不需要指定格式化程序。

String input = "2010-04-05T17:16:00Z";
Instant instant = Instant.parse ( input );

转储到控制台。

System.out.println ( "instant: " + instant );

瞬间:2010-04-05T17:16:00Z

从那里您可以应用时区 ( ZoneId) 将其调整InstantZonedDateTime. 搜索 Stack Overflow 以获取讨论和示例。

如果必须使用java.util.Date对象,可以调用添加到旧类中的新转换方法进行转换,例如静态方法java.util.Date.from( Instant )

java.util.Date date = java.util.Date.from( instant );

乔达时间

Joda-Time 2.5 中的示例。

DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" ):
DateTime dateTime = new DateTime( "2010-04-05T17:16:00Z", timeZone );

转换为 UTC。

DateTime dateTimeUtc = dateTime.withZone( DateTimeZone.UTC );

如有必要,转换为 java.util.Date。

java.util.Date date = dateTime.toDate();
于 2014-06-25T06:32:56.003 回答
32

在该模式中,包含“z”日期时间组件表示时区格式需要符合通用时区“标准”,例如Pacific Standard Time; PST; GMT-08:00.

“Z”表示时区符合RFC 822 时区标准,例如-0800.

我认为您需要一个 DatatypeConverter ...

@Test
public void testTimezoneIsGreenwichMeanTime() throws ParseException {
    final Calendar calendar = javax.xml.bind.DatatypeConverter.parseDateTime("2010-04-05T17:16:00Z");
    TestCase.assertEquals("gotten timezone", "GMT+00:00", calendar.getTimeZone().getID());
}
于 2010-04-05T20:47:36.737 回答
23

根据Java 7 API的日期和时间模式表的最后一行

X 时区 ISO 8601 时区 -08;-0800; -08:00

对于 ISO 8601 时区,您应该使用:

  • X 代表(-08 或 Z),
  • XX 代表(-0800 或 Z),
  • XXX 代表(-08:00 或 Z);

因此,要解析您的“2010-04-05T17:16:00Z”,您可以使用 “yyyy-MM-dd'T'HH:mm:ssX”或“yyyy-MM-dd'T'HH:mm:ssXX”或 "yyyy-MM-dd'T'HH:mm:ssXXX"

    System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX").parse("2010-04-05T17:16:00Z"));
    System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXX").parse("2010-04-05T17:16:00Z"));
    System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").parse("2010-04-05T17:16:00Z"));

将正确打印出'Mon Apr 05 13:16:00 EDT 2010'

于 2016-05-11T21:41:01.040 回答
7

'X' 仅在不存在部分秒数时才有效:即 SimpleDateFormat 模式

“yyyy-MM-dd'T'HH:mm:ssX”

将正确解析

“2008-01-31T00:00:00Z”

“yyyy-MM-dd'T'HH:mm:ss.SX”

不会解析

“2008-01-31T00:00:00.000Z”

可悲但真实的是,带有部分秒数的日期时间似乎不是有效的 ISO 日期:http ://en.wikipedia.org/wiki/ISO_8601

于 2015-03-23T12:23:50.047 回答
4

时区应该类似于 "GMT+00:00" 或 0000 以便被 SimpleDateFormat 正确解析 - 您可以用这种结构替换 Z。

于 2010-04-05T20:36:09.033 回答
4

在 Java 8 下使用预定义的 DateTimeFormatter.ISO_DATE_TIME

 DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
 ZonedDateTime result = ZonedDateTime.parse("2010-04-05T17:16:00Z", formatter);

我想这是最简单的方法

于 2017-05-03T08:25:19.093 回答
2

restlet 项目包括一个可以解析 RFC 3339 日期的 InternetDateFormat 类。

Restlet InternetDateFormat

不过,您可能只想在解析它之前将结尾的“Z”替换为“UTC”。

于 2010-04-05T20:57:27.530 回答
2

我提供了另一个api-client-library由 Google找到的答案

try {
    DateTime dateTime = DateTime.parseRfc3339(date);
    dateTime = new DateTime(new Date(dateTime.getValue()), TimeZone.getDefault());
    long timestamp = dateTime.getValue();  // get date in timestamp
    int timeZone = dateTime.getTimeZoneShift();  // get timezone offset
} catch (NumberFormatException e) {
    e.printStackTrace();
}

安装指南,
https://developers.google.com/api-client-library/java/google-api-java-client/setup#download

这是 API 参考,
https://developers.google.com/api-client-library/java/google-http-java-client/reference/1.20.0/com/google/api/client/util/DateTime

DateTimeClass的源代码,
https://github.com/google/google-http-java-client/blob/master/google-http-client/src/main/java/com/google/api/client/util/DateTime .java

DateTime单元测试,
https://github.com/google/google-http-java-client/blob/master/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java #L121

于 2016-03-17T08:15:47.283 回答
2

关于 JSR-310,另一个感兴趣的项目可能是threetenbp

JSR-310 为 Java SE 8 提供了一个新的日期和时间库。这个项目是 Java SE 6 和 7 的向后移植。

如果您正在开发一个Android项目,您可能需要查看ThreeTenABP 库

compile "com.jakewharton.threetenabp:threetenabp:${version}"

JSR-310 作为 java.time.* 包包含在 Java 8 中。它完全替代了 Java 和 Android 中出现问题的日期和日历 API。JSR-310 由其创建者 Stephen Colebourne 向后移植到 Java 6,该库就是从该库改编而来的。

于 2016-03-24T14:29:46.453 回答
1

由于 java 8 只是使用ZonedDateTime.parse("2010-04-05T17:16:00Z")

于 2017-11-06T01:12:47.967 回答