我有TimeZone
,比如说Asia/Kolkata
,如何在 Java 中获取相应的ZoneOffset
ID,即从中获取+05:30
?
我一直在尝试以下方法,但没有获胜:
String zoneOffsetID = ZoneId.of("Asia/Kolkata").getId();
ZoneOffset zoneOffset = ZoneOffset.of(zoneOffsetID);
我也尝试了Asia/Calcutta
我从
Set<String> allZones = ZoneId.getAvailableZoneIds();
这能得到你想要的:
String zoneOffsetID = ZoneId.of("Asia/Kolkata").getId();
System.out.println(zoneOffsetID);
ZoneOffset zoneOffset = ZoneId.of("Asia/Kolkata").getRules().getOffset(Instant.now());
System.out.println(zoneOffset);
。和打印:
Asia/Kolkata
+05:30
不确定这是否是最短/最整洁/首选的方式,但它有效。
正如许多人在评论中已经告诉的那样,API 没有没有参数的getOffset()
方法。这是因为时区不能保证只有一个偏移量。在其历史中,它们可以拥有多个(其中大多数 - 如果不是全部 - 都有)。
API 提供了一种检查所有这些更改的方法。
ZoneId
类有一个getRules()
方法,该方法返回一个java.time.zone.ZoneRules
实例,其中包含定义相应时区的偏移量如何随时间变化的所有规则。
让我们采用您在示例中使用的时区并获取其规则:
// get timezone
ZoneId zone = ZoneId.of("Asia/Kolkata");
// get rules
ZoneRules rules = zone.getRules();
// get list of transitions (date/times when the offset changes)
List<ZoneOffsetTransition> transitions = rules.getTransitions();
for (ZoneOffsetTransition zt : transitions) {
System.out.println(zt);
}
这将打印所有过渡:偏移量发生变化时的所有日期/时间。输出将是:
过渡[重叠在1880-01-01T00:00+05:53:28到+05:53:20]过渡[在1941-10-01T00:00+05:53:20到+06:30]过渡[重叠在1942-05-15T00:00+06:30到+05:30]过渡[在1942-09-01T00:00+05:30到+06:30的差距]过渡[重叠在1945-10-15T00:00+06:30到+05:30
]
让我们看一下最后一行。它说在1945年10月15日00h00,偏移量从+06:30
变为+05:30
。由于该时区夏令时结束,时钟向后移动了 1 小时(请参阅@MattJohnson在他的评论中提供的链接)
我们可以使用以下代码测试此行为:
// DST ends at 1945-10-15T00:00, setting date 1 minute before
ZonedDateTime dt = ZonedDateTime.of(1945, 10, 14, 23, 59, 0, 0, ZoneId.of("Asia/Kolkata"));
// in DST, offset is +06:30
System.out.println("in DST: " + dt);
// plus 1 minute, DST ends (offset changes to +05:30)
System.out.println("not DST: " + dt.plusMinutes(1));
输出为:
夏令时:1945-10-14T23:59+06:30[亚洲/加尔各答]请注意,在 10 月 14 日 23:59 中,偏移量为非夏令时:1945-10-14T23:00+05:30[亚洲/加尔各答]
+06:30
(采用 DST),但 1 分钟后(即 10 月 15 日 00:00),DST 结束,时钟向后移动 1 小时,因此它变为10月 14 日 23:00
,偏移量+05:30
。这就是为什么getOffset()
方法必须有一个Instant
参数:每个时刻都有不同的偏移量。
使用上面的日期,您可以使用ZoneRules.getOffset
方法检查偏移量,ZonedDateTime.toInstant()
方法将日期转换为Instant
:
ZoneId zone = ZoneId.of("Asia/Kolkata");
ZoneRules rules = zone.getRules();
ZonedDateTime dt = ZonedDateTime.of(1945, 10, 14, 23, 59, 0, 0, zone);
// in DST, offset is +06:30
System.out.println(rules.getOffset(dt.toInstant()));
// plus 1 minute, DST ends (offset changes to +05:30)
System.out.println(rules.getOffset(dt.plusMinutes(1).toInstant()));
输出为:
+06:30+05:30
让我们再次看一下此时区的转换列表:
过渡[重叠在1880-01-01T00:00+05:53:28到+05:53:20]过渡[在1941-10-01T00:00+05:53:20到+06:30]过渡[重叠在1942-05-15T00:00+06:30到+05:30]过渡[在1942-09-01T00:00+05:30到+06:30的差距]过渡[重叠在1945-10-15T00:00+06:30到+05:30
]
前 2 行表示加尔各答的时区在 1880 年之前使用+05:53:28
,然后一直使用+05:53:20
,直到 1941 年+06:30
偏移量开始。然后有一些DST转换,现在它没有DST,偏移量+05:30
。
因此,加尔各答的时区可以有四种不同的偏移量,具体取决于您要查找的日期和时间。如果你想知道时区的偏移量,你必须提供一个Instant
,因为API不能只在所有选项之间进行选择。要获得当前偏移量(目前正在使用的偏移量),您可以使用Instant.now()
(如 Robin 的答案)。
但不要忘记,政府可以随时更改时区,因此不能保证始终相同。某些政府可能会决定更改国家/地区的时区,或者说"今年我们将从明天开始使用夏令时",或类似的话。仅仅因为今天的时区没有 DST 变化,并不意味着它会一直这样。
这就是为什么我们不能有一个无参数getOffset()
方法。
还有一件事:您可以检查时区是否有偏移变化:
ZoneId zone = ZoneId.of("Asia/Kolkata");
ZoneRules rules = zone.getRules();
System.out.println(rules.isFixedOffset()); // false (not fixed = offset varies)
如果时区的偏移量从未更改,则isFixedOffset()
方法将返回true
。 但即使它是true
,API的设计者也决定getOffset
总是需要一个Instant
来检查那个时刻的偏移量(因为没有人知道偏移量将来是否会改变)。