夏季夏令时,ZonedDateTime会使用相同的冬季时区吗?

时间:2017-06-26 08:03:06

标签: java-8 dst java-time timezone-offset 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]

当我们将小时设置为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]

我们设置时,您可以看到夏令时时区为UTC-07:00,小时为0

为什么他们不同?

2 个答案:

答案 0 :(得分:7)

这是因为你选择的时间落在巴西切换到夏令时的午夜和01:00之间的差距。那个时间实际上是不可能的,所以你得到了documentation

中描述的行为
  

在间隙的情况下,当时钟向前跳跃时,没有有效的偏移。而是将本地日期时间调整为稍后的间隙长度。对于典型的一小时夏令时变化,本地日期时间将在一小时后移动到通常对应于"夏天"的偏移。

您可以通过在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); 

答案 1 :(得分:3)

正如@Misha's answer中已经解释的那样,它是由于夏令时规则而发生的。

在圣保罗,DST从2015-10-18的午夜开始:时钟向前移动1小时,因此它从23:59:59“跳跃”到01:00:0000:00:0000:59:59之间存在差距,因此时间00:30会相应调整。

您可以使用ZoneRulesZoneOffsetTransition类检查日期和时间是否对时区有效:

ZoneId sp = ZoneId.of("America/Sao_Paulo");
ZoneRules rules = sp.getRules();
// check if 2015-10-18 00:30 is valid for this timezone
LocalDateTime dt = LocalDateTime.of(2015, 10, 18, 0, 30);
List<ZoneOffset> validOffsets = rules.getValidOffsets(dt);
System.out.println(validOffsets.size()); // size is zero, no valid offsets at 00:30

getValidOffsets method返回指定日期/时间的所有有效偏移量。如果列表为空,则表示日期/时间在该时区内不“存在”(通常因为DST时钟向前跳跃)。

当时区中存在日期/时间时,将返回偏移量:

ZoneId la = ZoneId.of("America/Los_Angeles");
rules = la.getRules();
validOffsets = rules.getValidOffsets(dt);
System.out.println(validOffsets.size()); // 1 - date/time valid for this timezone
System.out.println(validOffsets.get(0)); // -07:00

对于Los_Angeles时区,返回1个有效偏移:-07:00

PS:由于DST,通常会发生偏移更改,但情况并非总是如此。 DST和抵消由政府和法律定义,它们可以随时改变。因此,有效抵消的差距也可能意味着发生了这种变化(一些政治家决定改变该国的标准偏差,因此差距可能不一定与DST有关。)

您还可以检查更改发生的时间,以及之前和之后的偏移量:

ZoneId sp = ZoneId.of("America/Sao_Paulo");
ZoneRules rules = sp.getRules();

// get the previous transition (the last one that occurred before 2015-10-18 00:30 in Sao_Paulo timezone 
ZoneOffsetTransition t = rules.previousTransition(dt.atZone(sp).toInstant());
System.out.println(t);

输出结果为:

  

过渡[2015-10-18T00:00-03:00至-02:00]的差距

这意味着2015-10-18T00:00处存在间隙(时钟向前移动),偏移量将从-03:00变为-02:00(因此,时钟向前移动1小时)。

您还可以单独获取所有这些信息:

System.out.println(t.getDateTimeBefore() + " -> " + t.getDateTimeAfter());
System.out.println(t.getOffsetBefore() + " -> " + t.getOffsetAfter());

输出结果为:

  

2015-10-18T00:00 - &gt; 2015-10-18T01:00
  -03:00 - &gt; -02:00

它表明,在00:00,时钟直接移至01:00(因此00:30不可存在)。在第二行中,更改前后的偏移量。

如果您检查Los_Angeles时区中的转换,您会看到其DST在不同日期开始和结束:

ZoneId la = ZoneId.of("America/Los_Angeles");
rules = la.getRules();

// 2015-10-18 00:30 in Los Angeles
Instant instant = dt.atZone(la).toInstant();
System.out.println(rules.previousTransition(instant));
System.out.println(rules.nextTransition(instant));

输出结果为:

  

过渡[2015-03-08T02:00-08:00至-07:00的差距]
  过渡[重叠于2015-11-01T02:00-07:00至-08:00]

因此,在Los_Angeles时区,DST从2015-03-08开始,到2015-11-01结束。这就是为什么在2015-10-18,所有小时都有效(Sao_Paulo时区没有调整)。

某些时区有转换规则(例如“DST从10月的第三个星期日开始”),而不仅仅是转换(例如“DST在此特定日期和时间开始”),如果可以的话,你也可以使用它们:

ZoneId sp = ZoneId.of("America/Sao_Paulo");
ZoneRules rules = sp.getRules();

// hardcoded: Sao_Paulo timezone has 2 transition rules, the second one is relative to October
// but you should always check if the list is not empty
ZoneOffsetTransitionRule tr = rules.getTransitionRules().get(1);
// get the transition for year 2015
ZoneOffsetTransition t = tr.createTransition(2015);
// use t the same way as above (the output will be the same)

检查某个时区的日期和时间是否有效的另一种方法是使用ZonedDateTime.ofStrict方法,如果某个时区的日期和时间无效,则会引发异常:

ZoneId sp = ZoneId.of("America/Sao_Paulo");
ZoneId la = ZoneId.of("America/Los_Angeles");
LocalDateTime dt = LocalDateTime.of(2015, 10, 18, 0, 30);

System.out.println(ZonedDateTime.ofStrict(dt, ZoneOffset.ofHours(-7), la)); // OK
System.out.println(ZonedDateTime.ofStrict(dt, ZoneOffset.ofHours(-3), sp)); // throws java.time.DateTimeException

第一种情况是可以的,因为对于给定的日期/时间,-7的偏移对洛杉矶有效。第二种情况引发异常,因为在给定的日期/时间,圣保罗的-3偏移无效。

相关问题