Java中的时区问题

问题描述 投票:0回答:2

我正在使用此简单代码来获取系统中已设置的当前时区的时区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

我将如何获得?

java windows timezone timezone-offset
2个回答
1
投票

要查找始终为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)));
}

0
投票

我的第一个答案是,您不必关心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,全年使用东部标准时间的地方(无夏令时/夏令时):

    [努纳武特-仅南安普敦岛(珊瑚港)(加拿大)(美国/珊瑚港时区)
  • [金塔纳罗奥州(墨西哥)(美国/坎昆时区)
  • 开曼群岛(美国/开曼)
  • 牙买加(美国/牙买加)
  • 巴拿马(美国/巴拿马)
  • [My Java另外列出了America / Atikokan(也在加拿大)。

    该列表不包括美国的任何地方。我相信以上都不是一直使用偏移量-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,仅作比较之用。哪些确实适合历史性日期,当然要取决于您对此的要求。)

    链接

  • List of tz database time zones
  • © www.soinside.com 2019 - 2024. All rights reserved.