除了时钟回滚一小时之外,可以通过编程方式确定时区,给定国家/地区代码和时区更改的时间列表。
例如,如果您知道在每个3月的最后一个星期日凌晨1点和10月凌晨2点,英国的时钟会发生变化,那么根据历史或未来的“本地”时间,您可以确定UTC时间。
我的问题是,Java中是否有任何可以确定此信息的内置方法 - 不仅是当前时间,还有历史时期?
答案 0 :(得分:2)
这并不是那么简单,因为时区的规则(可能/可以/做)一直在变化。
...如果您知道在每个三月的最后一个星期日凌晨1点和10月凌晨2点,英国的时钟会发生变化
所有历史日期都不是这样。 In the 70's你会发现一些DST的变化发生在3月中旬,而不是在最后一个星期天 - 就像在1974年和1975年,当时DST分别于3月17日和16日开始。另请注意,它从凌晨2点开始,而不是凌晨1点(only in 1981 it was changed to start at 1 AM)。
无论如何,Java包含来自IANA的所有时区数据(包括历史数据),因此您只需要使用内置类,API就可以完成所有数学运算:
// London timezone
ZoneId londonTimezone = ZoneId.of("Europe/London");
// the historical local date/time I want to check (March 16th 1975, 3 AM)
LocalDateTime localDt = LocalDateTime.of(1975, 3, 16, 3, 0, 0);
// convert to UK timezone (1975-03-16T03:00+01:00)
ZonedDateTime londonDt = localDt.atZone(londonTimezone);
// convert to UTC (1975-03-16T02:00:00Z)
Instant instant = londonDt.toInstant();
在上面的代码中,我创建了一个当地时间 1975年3月16日 th 凌晨3点(所以,after the DST transition, that occurred at 2 AM)。
当我将其转换为伦敦时区时,结果为1975-03-16T03:00+01:00
- 偏移量为+01:00(比UTC早一个小时),因为DST生效。将其转换为UTC,结果为1975-03-16T02:00:00Z
。
除了时钟回滚一小时......
实际上,有办法解决这个问题。使用London's DST transition in 1975,DST于10月26日结束 th :凌晨3点,时钟设置为1小时回到凌晨2点,因此凌晨2点到凌晨2点59分的所有当地时间都存在两次。如何解决这种歧义?
// London timezone
ZoneId londonTimezone = ZoneId.of("Europe/London");
// October 26th 1975, 2 AM - the ambiguous local time in London, due to DST end
LocalDateTime localDt = LocalDateTime.of(1975, 10, 26, 2, 0, 0);
// convert to UK timezone (default is the local time in DST: 1975-10-26T02:00+01:00)
ZonedDateTime londonDt = localDt.atZone(londonTimezone);
// get the date in DST (1975-10-26T02:00+01:00)
System.out.println(londonDt.withEarlierOffsetAtOverlap());
// get the date after DST ends (1975-10-26T02:00Z)
System.out.println(londonDt.withLaterOffsetAtOverlap());
方法withEarlierOffsetAtOverlap()
和withLaterOffsetAtOverlap()
分别返回DST结束前后的对应日期/时间,解决了歧义。
这些方法中的每一个都返回一个新的ZonedDateTime
实例(每个实例对应一个不同的UTC时刻),您可以在它们上面调用toInstant()
来获得相应的UTC时刻。
始终使用这两种方法获取转换前后的日期/时间。不要尝试增加或减少一小时,因为并非所有转换都会将时钟改变1小时:
可以以编程方式确定时区,给定国家/地区代码和时区更改的时间列表
这是可能的,但提醒一下,同一个国家可以有多个时区,特别是大国,如美国(大陆有4个区,加上阿拉斯加和夏威夷),俄罗斯(超过10个),巴西(4个 - 实际上是还有更多,因为有些州有DST但其他州没有),等等。
无论如何,根据国家/地区代码,您可以查看this file并获取该国家/地区的所有时区。然后,对于每个时区,运行与此类似的代码以比较转换发生时的所有日期:
ZoneId londonTimezone = ZoneId.of("Europe/London");
ZoneRules rules = londonTimezone.getRules();
// get all the transitions (dates when the offset changes)
rules.getTransitions().forEach(t -> {
// UTC instant when the change occurs
Instant instant = t.getInstant();
// local date/time before the transition
LocalDateTime before = t.getDateTimeBefore();
// UTC offset before the transition
ZoneOffset offsetBefore = t.getOffsetBefore();
// local date/time after the transition
LocalDateTime after = t.getDateTimeAfter();
// UTC offset afger the transition
ZoneOffset offsetAfter = t.getOffsetAfter();
// is GAP (clock shifts forward - local times are skipped)
boolean gap = t.isGap();
// is overlap (clock shifts backwards - local times can happen twice)
boolean overlap = t.isOverlap();
// *** You can use the data above to check if the transition date matches your list***
});
某些区域具有转换规则而不仅仅是转换,因此您还必须检查转换规则列表:
rules.getTransitionRules().forEach(rule -> {
// get a transition for specific year
ZoneOffsetTransition transition = rule.createTransition(2018);
// use the transition the same as above (getInstant(), getDateTimeBefore(), etc)
});