Hao*_* Ma 5 dst timezone-offset java-8 java-time zoneddatetime
ZonedDateTime zdt = ZonedDateTime.of(2015, 10, 18, 0, 30, 0, 0,
ZoneId.of("America/Sao_Paulo"));
System.out.println(zdt); // 2015-10-18T01:30-02:00[America/Sao_Paulo]
Run Code Online (Sandbox Code Playgroud)
您可以看到小时是1我们设置小时的时间0,而时区是UTC-02:00夏令时时区应该是UTC-03:00.
但这是一个不同的例子:
ZonedDateTime zdt = ZonedDateTime.of(2015, 10, 18, 0, 30, 0, 0,
ZoneId.of("America/Los_Angeles"));
System.out.println(zdt); //2015-10-18T00:30-07:00[America/Los_Angeles]
Run Code Online (Sandbox Code Playgroud)
您可以看到夏令时时区UTC-07:00,小时是0我们设置的.
他们为什么不同?
发生这种情况是因为你选择的时间落在巴西切换到夏令时的午夜和01:00之间的差距中.那个时间实际上是不可能的,所以你得到文档中描述的行为:
在间隙的情况下,当时钟向前跳跃时,没有有效的偏移.而是将本地日期时间调整为稍后的间隙长度.对于典型的一小时夏令时变化,本地日期时间将在一小时后移动到通常对应于"夏天"的偏移量.
您可以通过在3月的相应夜晚的02:00和03:00之间选择一个时间来观察Los_Angeles区域中的相同行为:
zdt = ZonedDateTime.of(2015, 3, 8, 2, 30, 0, 0,
ZoneId.of("America/Los_Angeles"));
System.out.println(zdt);
Run Code Online (Sandbox Code Playgroud)
小智 4
正如@Misha\'s 答案中已经解释的那样中已经解释的那样,这是由于夏令时规则而发生的。
\n\n在 S\xc3\xa3o Paulo,DST 从 2015 年 10 月 18 日午夜开始:时钟向前移动 1 小时,因此它从23:59:59跳到01:00:00。00:00:00和之间有差距00:59:59,因此时间00:30会相应调整。
ZoneRules您可以使用和类检查日期和时间对于时区是否有效ZoneOffsetTransition:
ZoneId sp = ZoneId.of("America/Sao_Paulo");\nZoneRules rules = sp.getRules();\n// check if 2015-10-18 00:30 is valid for this timezone\nLocalDateTime dt = LocalDateTime.of(2015, 10, 18, 0, 30);\nList<ZoneOffset> validOffsets = rules.getValidOffsets(dt);\nSystem.out.println(validOffsets.size()); // size is zero, no valid offsets at 00:30\nRun Code Online (Sandbox Code Playgroud)\n\n该getValidOffsets方法返回指定日期/时间的所有有效偏移量。如果列表为空,则意味着该时区中不“存在”日期/时间(通常是由于夏令时,时钟向前跳跃)。
当日期/时间存在于时区中时,返回偏移量:
\n\nZoneId la = ZoneId.of("America/Los_Angeles");\nrules = la.getRules();\nvalidOffsets = rules.getValidOffsets(dt);\nSystem.out.println(validOffsets.size()); // 1 - date/time valid for this timezone\nSystem.out.println(validOffsets.get(0)); // -07:00\nRun Code Online (Sandbox Code Playgroud)\n\n对于Los_Angeles时区,返回 1 个有效偏移量:-07:00。
PS:偏移量变化通常是由于夏令时而发生的,但情况并非总是如此。夏令时和补偿由政府和法律定义,并且可以随时更改。因此,有效偏移量的差距也可能意味着发生了这种变化(一些政治家决定改变国家的标准偏移量,因此差距可能不一定与 DST 有关)。
\n\n您还可以检查更改何时发生,以及更改前后的偏移量是多少:
\n\nZoneId sp = ZoneId.of("America/Sao_Paulo");\nZoneRules rules = sp.getRules();\n\n// get the previous transition (the last one that occurred before 2015-10-18 00:30 in Sao_Paulo timezone \nZoneOffsetTransition t = rules.previousTransition(dt.atZone(sp).toInstant());\nSystem.out.println(t);\nRun Code Online (Sandbox Code Playgroud)\n\n输出是:
\n\n\n\n\n过渡[2015-10-18T00:00-03:00 至 -02:00 之间的差距]
\n
这意味着在 处有一个间隙(时钟向前移动)2015-10-18T00:00,并且偏移量将从 变为-03:00到-02:00(因此,时钟向前移动 1 小时)。
您还可以单独获取所有这些信息:
\n\nSystem.out.println(t.getDateTimeBefore() + " -> " + t.getDateTimeAfter());\nSystem.out.println(t.getOffsetBefore() + " -> " + t.getOffsetAfter());\nRun Code Online (Sandbox Code Playgroud)\n\n输出是:
\n\n\n\n\n2015-10-18T00:00 -> 2015-10-18T01:00
\n
\n -03:00 -> -02:00
它表明,在00:00时钟直接移动到01:00(所以00:30不能存在)。第二行是更改前后的偏移量。
如果您检查时区的转换Los_Angeles,您将看到其 DST 在不同的日期开始和结束:
ZoneId la = ZoneId.of("America/Los_Angeles");\nrules = la.getRules();\n\n// 2015-10-18 00:30 in Los Angeles\nInstant instant = dt.atZone(la).toInstant();\nSystem.out.println(rules.previousTransition(instant));\nSystem.out.println(rules.nextTransition(instant));\nRun Code Online (Sandbox Code Playgroud)\n\n输出是:
\n\n\n\n\n过渡[2015-03-08T02:00-08:00 至 -07:00 之间的差距]
\n
\n 过渡[2015-11-01T02:00-07:00 至 -08:00 之间的重叠]
因此,在Los_Angeles时区中,DST 开始于2015-03-08结束于2015-11-01。这就是为什么在 时2015-10-18,所有时间都有效(没有调整,因为它发生在Sao_Paulo时区)。
某些时区具有转换规则(例如“DST 从十月的第三个星期日开始”),而不仅仅是转换(例如“DST 在这个特定日期和时间开始”),并且您也可以使用它们(如果有):
\n\nZoneId sp = ZoneId.of("America/Sao_Paulo");\nZoneRules rules = sp.getRules();\n\n// hardcoded: Sao_Paulo timezone has 2 transition rules, the second one is relative to October\n// but you should always check if the list is not empty\nZoneOffsetTransitionRule tr = rules.getTransitionRules().get(1);\n// get the transition for year 2015\nZoneOffsetTransition t = tr.createTransition(2015);\n// use t the same way as above (the output will be the same)\nRun Code Online (Sandbox Code Playgroud)\n\n检查日期和时间对于某些时区是否有效的另一种方法是使用该ZonedDateTime.ofStrict方法,如果日期和时间对于时区无效,该方法将引发异常:
ZoneId sp = ZoneId.of("America/Sao_Paulo");\nZoneId la = ZoneId.of("America/Los_Angeles");\nLocalDateTime dt = LocalDateTime.of(2015, 10, 18, 0, 30);\n\nSystem.out.println(ZonedDateTime.ofStrict(dt, ZoneOffset.ofHours(-7), la)); // OK\nSystem.out.println(ZonedDateTime.ofStrict(dt, ZoneOffset.ofHours(-3), sp)); // throws java.time.DateTimeException\nRun Code Online (Sandbox Code Playgroud)\n\n第一种情况是可以的,因为对于给定的日期/时间,偏移量-7对于洛杉矶来说是有效的。第二种情况会引发异常,因为-3在给定日期/时间, S\xc3\xa3o Paulo 的偏移量无效。