【问题标题】:Confusion with Java Time parsing UTC与 Java 时间解析 UTC 混淆
【发布时间】:2017-01-19 22:58:31
【问题描述】:

我对 java 时间中的时间处理感到困惑。我长期工作的假设是,如果将时间戳指定为祖鲁时间,java 会处理与本地时间有关的偏移量。

为了说明。我目前在 BST,其偏移量为 UTC +1。考虑到这一点,我希望这个祖鲁时间:

2016-09-12T13:15:17.309Z

成为

2016-09-12T14:15:17.309 

LocalDateTime 解析后。这是因为我的默认系统时间设置为 BST 并且上面的时间戳(祖鲁时间)指定它是 UTC 时间。

但请考虑以下示例:

        String ts = "2016-09-12T13:15:17.309Z";
        LocalDateTime parse = LocalDateTime.parse(ts, DateTimeFormatter.ISO_DATE_TIME);
        System.out.println(parse);

这将打印:

2016-09-12T13:15:17.309

因此,解析为 LocalDateTime 的时间戳不会被识别为 UTC 时间,而是直接被视为本地时间。 所以我想,也许我需要将它解析为 ZonedDateTime 并专门将其转换为 LocalDateTime 以获得正确的本地时间。通过这个测试:

        String ts = "2016-09-12T13:15:17.309Z";
        ZonedDateTime parse = ZonedDateTime.parse(ts, DateTimeFormatter.ISO_DATE_TIME);
        System.out.println(parse);
        System.out.println(parse.toLocalDateTime());

我得到了输出:

2016-09-12T13:15:17.309Z
2016-09-12T13:15:17.309

两个日期的输出相同。

我能找到的正确解析这个的唯一方法是:

    String ts = "2016-09-12T13:15:17.309Z";
    Instant instant = Instant.parse(ts); // parses UTC
    LocalDateTime ofInstant = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    System.out.println(instant);
    System.out.println(ofInstant);

打印出来:

2016-09-12T13:15:17.309Z
2016-09-12T14:15:17.309

哪个是正确的。

所以问题是:

  • Java 时间不应该识别 UTC 时间戳并将其解析为正确的系统默认值吗?
  • 如何使用LocalDateTime#parse 方法获得正确的结果?
  • 我现在应该对所有内容都使用Instant 并放弃解析吗?

问题在于jersey/jackson 的java 时间模块使用ISO 格式和常规LocalDateTime#parse 方法解析时间戳。我意识到我的时代并没有结束,因为它们被视为LocalTime,而实际上它们处于祖鲁时代。

【问题讨论】:

    标签: java parsing datetime java-time localtime


    【解决方案1】:

    你误解了LocalDateTime的目的。

    引用类文档:

    在 ISO-8601 日历系统中没有时区的日期时间,例如 {@code 2007-12-03T10:15:30}。

    此类不存储或表示时区。相反,它是对日期的描述,用于生日,结合挂钟上的当地时间。如果没有偏移或时区等附加信息,它不能代表时间线上的瞬间。

    所以它的明确目的只是表示一个日期和时间没有一个时区。建议表示日期和时间在当地时区

    因此,每次转换只是剥离时区。

    因此,出于您的目的,您需要一个 ZonedDateTimeZoneId.systemDefault(),正如您在第三个示例中已经使用的那样。

    对于您的第二个示例,这可能是:

    String ts = "2016-09-12T13:15:17.309Z";
    ZonedDateTime parse = 
        ZonedDateTime.parse(ts, DateTimeFormatter.ISO_DATE_TIME)
            .withZoneSameInstant(ZoneId.systemDefault());
    System.out.println(parse);
    System.out.println(parse.toLocalDateTime());
    

    【讨论】:

    • 您的示例看起来就像我的示例 3 的即时对话。您是对的,我误解了 LocalDateTime 的含义。呃.. 时区 :) 谢谢
    • 啊,所以 ZonedDateTime 将时区识别为“Z”,它会自动将其解释为 UTC(正是我想要的) - 对吗?
    • @pandaadb 是的,这可能只是写这篇文章的另一种方式。
    • @pandaadb 是的,它被解析器正确识别。只是转换为LocalDateTime 确实将其剥离。
    【解决方案2】:

    tl;博士

    例子:

    Instant.parse( "2016-09-12T13:15:17.309Z" )
           .atZone( ZoneId.of( "Europe/London" ) )
           .toString();
    

    2016-09-12T14:15:17.309+01:00[欧洲/伦敦]

    Run in IdeOne.com.

    详情

    Answer by Krüske 是正确的。您误解了LocalDateTime 类的含义。它确实代表特定地区的日期时间。恰恰相反,它确实代表一个实际的时刻。

    我建议将Instant 视为您在java.time 中的基本构建块类。 Instant 类代表UTC 时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

    您的输入字符串符合 Instant 类中默认使用的 ISO 8601 格式,用于解析和生成字符串表示。末尾的ZZulu 的缩写,表示UTC。无需指定格式模式。

    Instant instant = Instant.parse( "2016-09-12T13:15:17.309Z" );
    

    作为一名程序员,您应该学习主要在 UTC 中思考和工作。忘记你自己的时区。将 UTC 视为唯一的真实时间。仅根据需要应用时区作为变体。

    continent/region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 3-4 个字母的缩写,例如 BSTESTIST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。如果BST 是指英国夏令时,那么实际时区名称将是Europe/London。 java.time 类将确定如何针对包括夏令时 (DST) 在内的任何异常情况进行调整。

    ZoneId z = ZoneId.of( "Europe/London" );
    ZonedDateTime zdt = instant.atZone( z );
    

    关于java.time

    java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

    Joda-Time 项目现在位于 maintenance mode,建议迁移到 java.time。

    要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

    从哪里获得 java.time 类?

    ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-30
      • 2017-12-10
      • 2011-12-13
      • 2012-04-07
      • 2014-10-31
      • 1970-01-01
      • 2016-07-29
      相关资源
      最近更新 更多