Java如何获取给定时区缩写的时区ID列表

时间:2017-06-28 19:43:38

标签: java timezone simpledateformat java-time

是否可以找到给定时区缩写的时区ID列表?例如,对于缩写MainActivity,时区ID为ISTAsia/JerusalemAsia/Kolkata

3 个答案:

答案 0 :(得分:3)

有趣的问题。由于缩写没有标准化,因此无法获得权威答案,也无法获得这样的答案。我想到了两种方法:

  1. 从JVM中获取它们。
  2. 在网上找到它们。
  3. 从JVM获取区域:

        String givenAbbr = "IST";
        LocalDateTime summerSouthernHemisphere = LocalDate.of(2018, Month.JANUARY, 31).atStartOfDay();
        LocalDateTime summerNorthernHemisphere = LocalDate.of(2018, Month.JULY, 31).atStartOfDay();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("z");
        Set<ZoneId> zones = new HashSet<>();
        for (String id : ZoneId.getAvailableZoneIds()) {
            ZoneId zone = ZoneId.of(id);
            String abbr = summerSouthernHemisphere.atZone(zone).format(dtf);
            if (abbr.equals(givenAbbr)) {
                zones.add(zone);
            }
            abbr = summerNorthernHemisphere.atZone(zone).format(dtf);
            if (abbr.equals(givenAbbr)) {
                zones.add(zone);
            }
        }
        System.out.println(zones);
    

    打印:

    [Asia/Calcutta, Eire, Europe/Dublin, Asia/Jerusalem, Asia/Tel_Aviv, Israel, Asia/Kolkata, Asia/Colombo]
    

    其中一些只是同一时区的名称。例如,Eire与Europe / Dublin具有相同的规则。因此,如果需要,可以进行进一步的过滤。您可以使用oneZoneId.getRules().equals(anotherZoneId.getRules())来确定两个ZoneId对象是否具有相同的区域规则。

    对于缩写CST,列表甚至更长,并且有更多的同义词:

    [PRC, America/Matamoros, Asia/Taipei, America/Regina, America/El_Salvador,
            America/North_Dakota/New_Salem, Asia/Harbin, America/Costa_Rica,
            America/North_Dakota/Center, America/Guatemala, America/Winnipeg,
            Asia/Chongqing, America/Rankin_Inlet, America/Indiana/Knox,
            America/Belize, SystemV/CST6CDT, Mexico/General,
            America/North_Dakota/Beulah, CST6CDT, America/Swift_Current,
            America/Knox_IN, Asia/Chungking, Asia/Macao, Asia/Shanghai,
            America/Indiana/Tell_City, America/Menominee, America/Bahia_Banderas,
            America/Managua, Canada/East-Saskatchewan, Asia/Macau, America/Havana,
            America/Resolute, US/Central, US/Indiana-Starke, Cuba, America/Monterrey,
            America/Chicago, America/Merida, America/Mexico_City, Canada/Central,
            America/Tegucigalpa, America/Rainy_River, Canada/Saskatchewan, SystemV/CST6]
    

    我的方法的一个限制是一些时区被多个名称所知,因此超过一个三或四个字母的缩写。我上面的代码只捕获其中一个。

    另一个限制是选择像我这样的两个日期将永远不会给你过去和未来的所有可能性,甚至可能会错过一些我没有在正确日期到达的地方。我试图选择一个日期,在北半球是冬天,在南半球是夏天,而另一个是相反的日期。这将涵盖目前的大多数情况,但你永远不知道是否有一个时区或三个过渡不遵循我们所知的夏季和冬季。如果您想要更好的报道,Hugo’s answer中有一些很好的建议。

    从互联网上获取

    另一个答案当然是你的搜索毫无疑问已经提出的答案:这些列表在互联网上是公开的。例如Wikipedia’s List of time zone abbreviationsTime Zone Abbreviations – Worldwide List on timeanddate.com。正如所料,提到的两个名单不同意。例如,后者知道ADT的两种解释,前者只有一种。后面的列表给出了许多同义词缩写,从而说明了我的观点,即每个区域可以有多个缩写。

答案 1 :(得分:1)

@Ole V.V.'s answer已经提供了非常详细的信息,例如3个字母的缩写(例如ISTPST)是ambiguous and not standard,您应该更喜欢长IANA timezones names(格式为Continent/City,格式为Asia/KolkataEurope/Berlin)。

但是答案中有一个棘手的细节:它将2018年1月和7月作为冬季和夏季的基准日期(因此可以根据标准和夏令时期间检查缩写)。但是并不能保证所有情况都需要冬季和夏季时间,因为时区规则可以改变 - 仅仅因为时区今天有DST,这并不意味着它会永远拥有它(反之亦然)。

因此,不是选择一些日期/时间并希望所有时区之间都有DST更改,最好的方法是从ZoneRules对象获取所有更改 - 它包含所有转换日期(该时区的偏移量发生变化的时刻 - 由于DST开始/结束或因为某些政府决定他们的国家现在将在另一个时区)。

它还涵盖了时区在过去使用缩写但随后更改为其他时区的情况,因为我正在检查所有时区的更改历史记录。

代码非常相似。唯一的区别是,我没有使用固定日期(2018年1月/ 7月),而是查看区域的所有过渡(如果时区没有过渡 - 这意味着它从未有过DST或任何其他变化 - 我得到了当前的日期)。我还创建了Set String(因为您只需要名称,但您也可以存储ZoneId个对象):

String ist = "IST";
Set<String> zones = new HashSet<>();
// formatter to get the abbreviation
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("z");
for (String id : ZoneId.getAvailableZoneIds()) {
    ZoneId zone = ZoneId.of(id);
    ZoneRules rules = zone.getRules();
    List<ZoneOffsetTransition> transitions = rules.getTransitions();
    if (transitions.isEmpty()) {
        // no transitions found, just pick any date
        String abbrev = fmt.format(ZonedDateTime.now(zone));
        if (ist.equals(abbrev)) {
            zones.add(id);
        }
    } else {
        for (ZoneOffsetTransition transition : transitions) {
            // get the instant that the transition occurred and convert to this zone
            String abbrev = fmt.format(transition.getInstant().atZone(zone));
            if (ist.equals(abbrev)) {
                zones.add(id);
            }
        }
    }
}

System.out.println(zones);

在这种情况下,输出将是相同的:

  

[Asia / Calcutta,Eire,Europe / Dublin,Asia / Jerusalem,Asia / Tel_Aviv,Israel,Asia / Kolkata,Asia / Colombo]

虽然这段代码看起来更加冗余(因为它遍历了发生DST更改时的所有日期),但更能保证获得所有案例。如果您查找过去曾有DST的时区,但在2018年(或任何其他任意日期)没有,则使用此任意日期将无效。只有通过检查所有转换,您才能确保涵盖所有案例。

一个例子:如果不是IST,我想查看缩写AEDT(澳大利亚东部夏令时)。

使用@Ole V.V.的代码,我会得到:

  

[澳大利亚/悉尼,澳大利亚/墨尔本,澳大利亚/霍巴特,澳大利亚/维多利亚/ ACT,澳大利亚/堪培拉,澳大利亚/新南威尔士州,澳大利亚/塔斯马尼亚,澳大利亚/库里]

使用我的代码,我会得到:

  

[澳大利亚/悉尼,澳大利亚/布里斯班,澳大利亚/墨尔本,澳大利亚/昆士兰,澳大利亚/霍巴特,澳大利亚/维多利亚,澳大利亚/ ACT,澳大利亚/堪培拉,澳大利亚/新南威尔士州,澳大利亚/塔斯马尼亚州,澳大利亚/柯里,澳大利亚/林德曼]

注意差异。一个例子是Australia/Brisbanehad DST until the 90's,但现在它没有(所以it won't have it in 2018也是如此)。所以,如果你试图在2018年获得AEDT(夏令时),@ Ole V.V.的代码将不会选择这个时区,因为它在2018年不会有DST。

但我正在检查历史上所有的变化,无论何时发生。这保证我可以覆盖所有案件。

PS:如果你想获得在特定日期有效的缩写,那么你可以使用@Ole V.V.的代码(只需相应地改变日期)。

另一种方式(不容易)是下载IANA Time Zone Database file并关注this tutorial以了解如何阅读文件(非简单,IMO)。举例来说,都柏林的条目:

# Zone  NAME        GMTOFF  RULES   FORMAT  [UNTIL]
Zone    Europe/Dublin   -0:25:00 -  LMT 1880 Aug  2
            -0:25:21 -  DMT 1916 May 21  2:00 # Dublin MT
            -0:25:21 1:00   IST 1916 Oct  1  2:00s
             0:00   GB-Eire %s  1921 Dec  6 # independence
             0:00   GB-Eire GMT/IST 1940 Feb 25  2:00
             0:00   1:00    IST 1946 Oct  6  2:00
... etc

您可以看到IST用于Europe/Dublin。嗯,这不是最简单的方法,但每次IANA更新其数据库时,都需要一些时间才能将更改包含在JDK中(尽管如果您愿意,可以update just the timezone data)。

因此,如果您想了解最新信息,可以定期查看IANA网站上的更新。

答案 2 :(得分:-1)

请参阅oracle docs上的 ZoneDateTime api 的Java 8文档。

实施此方法的github上的示例maven项目的

Link

明智的实施,你可以使用下面的代码,

ZoneId losAngeles = ZoneId.of("America/Los_Angeles");
ZoneId berlin = ZoneId.of("Europe/Berlin");

// 2014-02-20 12:00
LocalDateTime dateTime = LocalDateTime.of(2014, 02, 20, 12, 0);

// 2014-02-20 12:00, Europe/Berlin (+01:00)
ZonedDateTime berlinDateTime = ZonedDateTime.of(dateTime, berlin);

// 2014-02-20 03:00, America/Los_Angeles (-08:00)
ZonedDateTime losAngelesDateTime = berlinDateTime.withZoneSameInstant(losAngeles);

int offsetInSeconds = losAngelesDateTime.getOffset().getTotalSeconds(); // -28800

// a collection of all available zones
Set<String> allZoneIds = ZoneId.getAvailableZoneIds();
相关问题