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:ssXX
或yyyy-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 SimpleDateFormat:模式 - ParseException [重复]