与Java时间混淆解析UTC

pan*_*adb 9 java datetime parsing localtime java-time

我对java时间的时间处理感到困惑.我长期以来一直在假设如果将时间戳指定为祖鲁时间,那么java会处理与本地时间有关的偏移量.

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

2016-09-12T13:15:17.309Z
Run Code Online (Sandbox Code Playgroud)

成为

2016-09-12T14:15:17.309 
Run Code Online (Sandbox Code Playgroud)

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

相反,请考虑以下示例:

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

这将打印:

2016-09-12T13:15:17.309
Run Code Online (Sandbox Code Playgroud)

因此,解析为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());
Run Code Online (Sandbox Code Playgroud)

我得到了输出:

2016-09-12T13:15:17.309Z
2016-09-12T13:15:17.309
Run Code Online (Sandbox Code Playgroud)

两个日期的输出相同.

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

    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);
Run Code Online (Sandbox Code Playgroud)

这打印:

2016-09-12T13:15:17.309Z
2016-09-12T14:15:17.309
Run Code Online (Sandbox Code Playgroud)

哪个是对的.

所以问题是:

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

问题是jersey/jacksonjava时间模块使用ISO格式和常规LocalDateTime#parse方法解析时间戳.我意识到我的时代已经没有了,因为他们正在接受治疗LocalTime,事实上他们在祖鲁时代.

Mic*_*ner 9

你误解了目的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());
Run Code Online (Sandbox Code Playgroud)


Bas*_*que 6

tl; dr

例:

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

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

在IdeOne.com中运行

细节

克鲁斯克答案是正确的。您误解了LocalDateTime类的含义。它并不能代表一个特定地点的日期-时间。正好相反,它并不能代表所有实际的时刻。

我建议将其Instant视为java.time中的基本构建块类。该Instant级表示时间轴上的时刻UTC,分辨率为纳秒(最多小数的9个位数)。

您的输入字符串符合Instant该类中默认用于解析和生成字符串表示形式的ISO 8601格式。在Z上月底是短期的Zulu,并指UTC。无需指定格式模式。

Instant instant = Instant.parse( "2016-09-12T13:15:17.309Z" );
Run Code Online (Sandbox Code Playgroud)

作为程序员,您应该首先学习在UTC中进行思考和工作。忘记自己的时区。将UTC视为唯一的真实时间。仅在需要时才应用时区作为变体。

指定适当的时区名称,格式continent/region,如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用3-4字母缩写,例如BSTESTIST因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。如果按BST您的意思是英国夏令时,则实际时区名称将为Europe/London。java.time类将确定如何调整任何异常,包括夏令时(DST)。

ZoneId z = ZoneId.of( "Europe/London" );
ZonedDateTime zdt = instant.atZone( z );
Run Code Online (Sandbox Code Playgroud)

关于java.time

java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar,和SimpleDateFormat

现在处于维护模式Joda-Time项目建议迁移到java.time。

要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

在哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多