与Java时间混淆解析UTC

时间:2016-09-12 13:25:49

标签: java parsing datetime java-time localtime

我对java时间的时间处理感到困惑。我长期以来的工作是假设如果时间戳被指定为祖鲁时间,那么java会处理关于本地时间的偏移量。

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

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,而实际上他们在祖鲁时代。

2 个答案:

答案 0 :(得分:7)

您误解了LocalDateTime的目的。

引用课程文档:

  

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

     

...

     

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

因此,明确的目的只是表示没有时区的日期和时间。它是来表示当地时区的日期和时间

因此,每次转换都会删除时区。

因此,出于您的目的,您需要使用ZonedDateTime ZoneId.systemDefault(),因为您已经在第三个示例中使用过了。{/ p>

对于你的第二个例子,这可能是:

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());

答案 1 :(得分:5)

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类的含义。 表示特定地区的日期时间。恰恰相反, not 根本不代表实际时刻。

我建议将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。切勿使用诸如BSTESTIST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。如果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.DateCalendar和& SimpleDateFormat

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

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore