【问题标题】: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

标签: java datetime parsing timezone simpledateformat


【解决方案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 课程。
    • 谢谢,我以前没看过这门课。似乎在标准 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();
    

    【讨论】:

    【解决方案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 APIDate 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")

          【讨论】:

            猜你喜欢
            • 2021-06-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-05-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多