如果没有正确格式化DST,我如何获得时区偏移?

时间:2017-06-06 19:01:11

标签: java timezone java-time timezone-offset

我没有找到一种方法来获得没有夏令时干扰的时区偏移。

LocalDateTime dt = LocalDateTime.now();
...
ZoneId zone = ZoneId.of(s);
ZonedDateTime zdt = dt.atZone(zone);
ZoneOffset offset = zdt.getOffset();

这似乎有效,但初始DataTime可能会受到DST的影响。

还有:

int rawOffset = tZone.getRawOffset();

但是,这总是一个正数,所以我们永远不会得到-05:00

所以我想要的是一种在没有DST干扰的情况下获得偏移的方法。有什么想法吗?

3 个答案:

答案 0 :(得分:3)

使用ZoneRules.getStandardOffset(Instant)

ZoneId zone = ZoneId.of(s);
ZoneRules rules = zone.getRules();
ZoneOffset standardOffset = rules.getStandardOffset(Instant.now());

答案 1 :(得分:0)

根据Java API文档,TimeZone.getRawOffset()将返回正确的历史偏移量。

  

public abstract int getRawOffset()

     

返回添加到UTC以获取此时区的标准时间的时间量(以毫秒为单位)。由于此值不受夏令时影响,因此称为原始偏移。

     

如果基础TimeZone实现子类支持历史GMT偏移量更改,则该方法返回当前日期的原始偏移值。例如,在檀香山,它的原始偏移从1947年的GMT-10:30变为GMT-10:00,这种方法总是返回-36000000毫秒(即-10小时)。

     

返回:      添加到UTC的原始偏移时间量(以毫秒为单位)。   也可以看看:      Calendar.ZONE_OFFSET

来自:https://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html#getRawOffset()

答案 2 :(得分:0)

首先,请记住,抵消会随着时间的推移而变化(甚至是标准的),主要是因为政府,法律和其他外部因素。

这就是为什么API需要Instant作为参考:知道在特定时刻的标准偏移是什么(无论Instant是过去,现在还是未来)。如果不及时了解,您无法说出偏移量。这就是@Joachim's answer是正确的原因。

但正如您在评论中所说,您不想依赖Instant.now(),我已经完成了以下代码 - 请记住,解决方案并非100%理想,因为上面的原因。它是我遵循你的标准的最好的。

您可以使用java.time.zone.ZoneRules课程来完成此操作,您可以直接从ZoneId实例获取该课程:

// pick a timezone with DST
ZoneId zone = ZoneId.of("America/Sao_Paulo");
// get rules
ZoneRules rules = zone.getRules();
// check if offset changes over time (if it returns false, it means is not fixed, so it has DST)
System.out.println(rules.isFixedOffset()); // false

在上面的代码中,rules.isFixedOffset()会返回false,这意味着相应的时区偏移会随着时间的推移而变化(因此,它会有DST更改)。

要知道何时发生DST更改,您必须获得所有转换:

// get all transitions (all dates where DST starts or ends)
List<ZoneOffsetTransition> transitions = rules.getTransitions();
transitions.stream().forEach(System.out::println);

这会打印很多行(因为这个时区几乎每年都有DST)。这是最后两行的样本:

  

转换[重叠于2039-02-20T00:00-02:00至-03:00
  过渡[在2039-10-16T00:00-03:00到-02:00之间的差距]

这意味着在午夜的 2039-02-20 中,偏移量将从-02:00更改为-03:00(DST结束 - 时钟向后移动1小时),并且在 2039-10-16 午夜,偏移量将从-03:00变为-02:00(DST开始 - 时钟向前移动1小时)。

所以,如果你想知道它不是夏令时的偏移量,你只需要进行最后一次转换,检查它是否是间隙或重叠并在之前或之后得到偏移:

// get the last transition
ZoneOffsetTransition last = transitions.get(transitions.size() - 1);

// find the offset without DST interference
ZoneOffset offsetWithoutDST;

// overlap means that clock moves back 1 hour (when DST ends) 
if(last.isOverlap()) {
    // DST ends, get the offset after it
    offsetWithoutDST = last.getOffsetAfter();
} else {
    // DST starts, get the offset before it
    offsetWithoutDST = last.getOffsetBefore();
}
System.out.println(offsetWithoutDST);

在这种情况下,它会打印-03:00,这是&#34;正常&#34; America/Sao_Paulo时区的偏移量(当它不是DST时)。

PS:请注意,DST甚至标准偏移量会随着时间的推移而变化很大。

仅仅因为今天的偏移是一个,它并不意味着它总是一样的:政府可以随时改变偏移,并且在过去的日期 - 比如1900年之前 - 抵消比今天(每个城市都有自己的时间,有-03:06:28等偏移量。)

上面的代码获取当前时区规则的标准(非DST)偏移量。如果你想知道30年前的非DST偏移是什么,你可以使用@Joachim's answer(通过30年前的Instant作为参数)。