我正在使用此简单代码来获取系统中已设置的当前时区的时区ID。
public class Main {
public static void main(String[] args)
{ TimeZone timezone = TimeZone.getDefault();
System.out.println(timezone.getID());
}
}
我使用tzutil /g
确定时区,并使用tzutil /s
设置时区。我观察到,当时区设置为Eastern Standard Time时,上述代码将id返回为America/New_York
,但将其设置为Eastern Standard Time_dstoff时,我得到的id为GMT-05:00
,这是不希望的。我希望输出形式为continent / city。
我将如何获得?
要查找始终为GMT-05:00
或aka 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)));
}
我的第一个答案是,您不必关心JVM的默认时区设置。无论如何,您的Java代码都不应该依赖它。可以随时从程序的另一部分或在同一JVM中运行的另一个程序随时更改默认设置,因此该设置很脆弱。而是在对时区敏感的操作中指定显式时区。
如果您仍然想使用默认值,那么如果碰巧是您认为不希望使用的GMT-05:00
,则可以将其设置为某个[[continent / city时区。
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));
还有另外一个警告,警告您不要使用America / Panama或其他时区ID代替GMT-05:00:尽管从1908年以来,巴拿马一直在使用GMT / UTC的偏移量-05:00,但它一直没有并非总是如此,因此对于历史性日期,使用它会得到错误的时间。根据timeanddate.com,全年使用东部标准时间的地方(无夏令时/夏令时):
该列表不包括美国的任何地方。我相信以上都不是一直使用偏移量-05:00
。也就是说,它们每个在历史记录的某个时刻都有不同的偏移量,而Java会将不同的偏移量用于历史日期。
那么要使用哪个时区?
Edit: 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
((您的出发点,格林尼治标准时间-05:00,仅作比较之用。哪些确实适合历史性日期,当然要取决于您对此的要求。)链接
timeanddate.com上的[EST – Eastern Standard Time / Eastern Time (Standard Time)。