SimpleDateFormat用'Z'文字解析日期[重复]

Posted

技术标签:

【中文标题】SimpleDateFormat用\'Z\'文字解析日期[重复]【英文标题】:SimpleDateFormat parsing date with 'Z' literal [duplicate]SimpleDateFormat用'Z'文字解析日期[重复] 【发布时间】:2011-02-04 13:45:25 【问题描述】:

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

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 上使用以获得预期的输出,但我认为这没有必要。有什么我想念的吗?是否有替代日期解析器?

【问题讨论】:

时间戳中的“Z”后缀不应与模式中的 Z 或 z 混淆。在 java 7 中,您可以使用“X”模式字母解析带有 SimpleDateFormat 的 ISO8601 后缀。 仅供参考,麻烦的旧日期时间类,如 SimpleTextFormat 现在是 legacy,被 java.time 类取代。见Tutorial by Oracle。 'Z' is not the same as Z 【参考方案1】:

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

【讨论】:

【参考方案2】:

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

“Z”表示时区符合RFC 822 time zone 标准,例如-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());

【讨论】:

是的,我明白这一点。 z/Z 排列只是让我看看是否有任何东西会粘住。我的日期仍然有效,并且应该有一个有效的模式来解析它。 我很难记住一位同事最近才向我展示的 DatatypeConverter 课程。 另见之前的***.com/questions/2201925/…答案 谢谢,我以前没看过这门课。似乎在标准 jdk 类中终于有了一个 BASE64 en/decoder :) 格式字符串中带有“X”的第三个答案是最正确/最新的。【参考方案3】:

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

Restlet InternetDateFormat

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

【讨论】:

Restlet InternetDateFormat 对我来说效果很好【参考方案4】:

Java 无法正确解析 ISO 日期。

与 McKenzie 的回答类似。

在解析之前修复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

【讨论】:

太可惜了!感谢您的提示 Java 7 添加了模式字母 X 来解析 ISO 日期。 @Dave,感谢您的更新! "Java 无法正确解析 ISO 日期。" -- 好吧,旧的 Java 没有。但是,新 Java 可以(Java 8 及更高版本)。请参阅新的 java.time 框架。 不幸的是,我们中的很多人都必须支持较旧的 Java,例如 android 7 之前的 Android 手机。(Z 模式字母在 Android 6 上引发异常。)【参考方案5】:

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

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

【讨论】:

这应该是正确的答案,而不是接受的答案。 另外,如果您想解析时区的分钟数,只需添加第二个或第三个X:yyyy-MM-dd'T'HH:mm:ssXXyyyy-MM-dd'T'HH:mm:ssXXX。更多详情见SimpleDateFormat - ISO 8601 Time zone。 这应该是该问题的任何其他高度投票答案最相关的答案【参考方案6】:

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 项目扩展。见Tutorial。

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

输入字符串末尾的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) 以将此 Instant 调整为 ZonedDateTime。搜索 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();

【讨论】:

请注意 Stephen Colebourne 的这篇文章:Why JSR-310 isn't Joda-Time【参考方案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

【讨论】:

这太烦人了。 引用的 wiki 页面引用:“小数部分可以添加到三个时间元素中的任何一个。[...] 小数部分的小数位数没有限制。但是,小数位数需要通信双方同意。” “Z”是时区的独立概念。所以部分秒是有效的 ISO 日期。解析会很好。 (也许它不是“简单日期格式”?;))【参考方案8】:

我提供了我在 Google 上通过 api-client-library 找到的另一个答案

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

【讨论】:

【参考方案9】:

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

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

如果您正在处理 Android 项目,您可能需要查看ThreeTenABP library。

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

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

【讨论】:

【参考方案10】:

根据Java 7 API的Date and Time Patterns表的最后一行

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”

【讨论】:

这应该是公认的答案,因为它显示了如何使用 SimpleDateFormat 解析日期字符串,就像问的问题一样。【参考方案11】:

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

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

我想这是最简单的方法

【讨论】:

【参考方案12】:

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

【讨论】:

以上是关于SimpleDateFormat用'Z'文字解析日期[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Java转换解析中间带有 “T“和“Z“ 的时间格式

Java SimpleDateFormat:模式 - ParseException [重复]

在 Java 中解析 Youtube API 日期 [重复]

某些字符串的 DateFormat 模式

JS 日期格式化和解析工具

SimpleDateFormat 格式化 解析