[英]Time zone issue in Java
我正在使用这个简单的代码来获取系统中设置的当前时区的时区 ID。
public class Main {
public static void main(String[] args)
{ TimeZone timezone = TimeZone.getDefault();
System.out.println(timezone.getID());
}
}
我使用tzutil /g
来确定时区,使用tzutil /s
来设置时区。 我观察到当时区设置为东部标准时间时,上面的代码返回 id 为America/New_York
但是当它设置为东部标准时间_dstoff 时,我得到的 id 为GMT-05:00
,这是不可取的。 我想要continent/city形式的输出。
我怎么会得到那个?
我的第一个答案是您不应该关心 JVM 的默认时区设置。 无论如何,您的 Java 代码不应依赖它。 可以随时从程序的其他部分或在同一 JVM 中运行的其他程序更改默认设置,因此很脆弱。 而是在时区敏感操作中指定显式时区。
如果您仍然想依赖默认值,如果碰巧是您认为不受欢迎的GMT-05:00
,这里有一个技巧可以将其设置为某个大陆/城市时区。
ZoneId defaultZoneId = ZoneId.systemDefault();
ZoneId desiredTimeZone = null;
if (defaultZoneId.equals(ZoneId.of("GMT-05:00"))) {
// Pick a date in summer
LocalDate dateInSummer = Year.now().atMonthDay(MonthDay.of(Month.JULY, 7));
DateTimeFormatter zoneFormatter = DateTimeFormatter.ofPattern("zzzz", Locale.ENGLISH);
for (String zid : ZoneId.getAvailableZoneIds()) {
ZoneId zone = ZoneId.of(zid);
String timeZoneName = dateInSummer.atStartOfDay(zone).format(zoneFormatter);
if (zid.startsWith("America/") && timeZoneName.equals("Eastern Standard Time")) {
desiredTimeZone = zone;
break;
}
}
}
System.out.println("Time zone: " + desiredTimeZone);
在我的 JDK 11 上,我得到了:
时区:美洲/巴拿马
不幸的是,要将其设置为默认值,我们必须通过设计不佳且早已过时的TimeZone
类:
TimeZone.setDefault(TimeZone.getTimeZone(desiredTimeZone));
还有一个理由警告不要使用美国/巴拿马或其他时区 ID 而不是 GMT-05:00:虽然巴拿马自 1908 年的某个时间以来一直使用 GMT/UTC 的偏移量 -05:00,但它并没有总是如此,因此对于历史日期,您会因使用它而得到错误的时间。
根据 timeanddate.com,全年使用东部标准时间(无夏令时/夏令时)的地方是:
我的 Java 还列出了 America/Atikokan(也在加拿大)。
该列表不包括美国的任何地方。 我相信以上都没有一直使用偏移量-05:00
。 也就是说,它们中的每一个在历史上的某个时刻都有不同的偏移量,Java 将对历史日期使用不同的偏移量。
编辑: Andreas 在评论中指出 Etc/GMT+5 和 SystemV/EST5 都没有我提到的关于将偏移应用于可能出乎意料的历史日期的问题。 所以你可以考虑其中之一。 在我看来,每个人的优点是:
America/Xxx Etc/GMT+5 SystemV/EST5 GMT-05:00
Official IANA time zone? Yes Yes
With slash? Yes Yes Yes
Continent/city? Yes
Good for historical dates? Yes Yes Yes
Prints as EST/Eastern Standard Time? Yes Yes
(您的起点 GMT-05:00 仅用于比较。当然,哪些对历史日期非常有用取决于您对这些日期的要求。)
要查找一直是GMT-05:00
,又名Eastern Standard Time_dstoff
,请检查所有ZoneId
规则以找到具有以下内容的时区:
-5 小时的偏移
没有过渡(过去的时区变化)
无转换规则(当前和未来时区更改)
for (String id : ZoneId.getAvailableZoneIds()) {
ZoneRules rules = ZoneId.of(id).getRules();
if (rules.getTransitions().isEmpty() && rules.getTransitionRules().isEmpty()
&& rules.getOffset(Instant.now()).getTotalSeconds() == 5 * -3600) {
System.out.println(id);
}
}
输出(Windows 上的 OpenJDK 13.0.2)
Etc/GMT+5
SystemV/EST5
因此,如果您需要将时区从GMT-05:00
更改为带有/
的时区,请使用这两者之一。 Etc/GMT+5
似乎合适。
如果你想自动化它,所以它会在默认时区不包含/
时尝试修复它,那么这样的事情可能会起作用:
if (! ZoneId.systemDefault().getId().contains("/")) {
int offset = ZoneId.systemDefault().getRules().getOffset(Instant.now()).getTotalSeconds();
String candidate = null;
for (String id : ZoneId.getAvailableZoneIds()) {
ZoneRules rules = ZoneId.of(id).getRules();
if (rules.getTransitions().isEmpty() && rules.getTransitionRules().isEmpty()
&& rules.getOffset(Instant.now()).getTotalSeconds() == offset) {
if (id.startsWith("Etc/GMT")) {
candidate = id;
break;
}
if (candidate == null)
candidate = id;
}
}
if (candidate != null)
TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of(candidate)));
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.