关于java:simpledateformat使用’Z’文字解析日期

simpledateformat parsing date with 'Z' literal

本问题已经有最佳答案,请猛点这里访问。

我试图解析一个看起来像这样的日期:

1
2010-04-05T17:16:00Z

这是http://www.ietf.org/rfc/rfc3339.txt的有效日期。 'Z'字面意思"暗示UTC
是指定时间的首选参考点。"

如果我尝试使用SimpleDateFormat和此模式解析它:

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

它将被解析为2010年4月5日17:16:00美国东部时间2010年

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

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

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


Java无法正确解析ISO日期。

与McKenzie的答案类似。

只需在解析之前修复Z即可。

1
2
3
4
5
6
7
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));

结果

1
2
3
string: 2013-03-05T18:05:05.000Z
defaultTimezone: America/New_York
date: 2013-03-05T13:05:05.000-0500


您要解析的日期是ISO8601格式。

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


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

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

我想你需要一个DatatypeConverter ......

1
2
3
4
5
@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());
}


TL;博士

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

ISO 8601标准

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

避免j.u.Date

与Java捆绑在一起的java.util.Date和.Calendar类非常麻烦。避免他们。

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

java.time

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

java.time中的Instant类表示UTC时区中时间轴上的一个时刻。

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

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

转储到控制台。

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

instant: 2010-04-05T17:16:00Z

从那里,您可以应用时区(ZoneId)将此Instant调整为ZonedDateTime。搜索Stack Overflow以进行讨论和示例。

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

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

乔达时间

Joda-Time 2.5中的示例。

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

转换为UTC。

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

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

1
java.util.Date date = dateTime.toDate();


根据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"。

1
2
3
    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'


'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


时区应该是"GMT + 00:00"或0000,以便由SimpleDateFormat正确解析 - 您可以用这种结构替换Z.


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

JSR-310 provides a new date and time library for Java SE 8. This project is the backport to Java SE 6 and 7.

如果您正在处理Android项目,则可能需要签出ThreeTenABP库。

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

JSR-310 was included in Java 8 as the java.time.* package. It is a full replacement for the ailing Date and Calendar APIs in both Java and Android. JSR-310 was backported to Java 6 by its creator, Stephen Colebourne, from which this library is adapted.


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

1
2
3
4
5
6
7
8
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

源代码DateTime类,
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


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

Restlet InternetDateFormat

但是,您可能只想在解析之前将"Z"替换为"UTC"。


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

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

我想这是最简单的方法


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