Java如何获取时区ID列表’;s表示给定的时区缩写

Java如何获取时区ID列表’;s表示给定的时区缩写,java,timezone,simpledateformat,java-time,Java,Timezone,Simpledateformat,Java Time,是否可以找到给定时区缩写的时区ID列表?例如,对于缩写词IST,时区ID是亚洲/耶路撒冷,亚洲/加尔各答和欧洲/都柏林有趣的问题。由于缩略语没有标准化,因此不可能有权威的答案,也不可能有可靠的方法来获得这样的答案。我突然想到了两种方法: 从JVM中获取它们 在网上找到他们 从JVM获取区域: String givenAbbr = "IST"; LocalDateTime summerSouthernHemisphere = LocalDate.of(2018, Month.JAN

是否可以找到给定时区缩写的时区ID列表?例如,对于缩写词
IST
,时区ID是
亚洲/耶路撒冷
亚洲/加尔各答
欧洲/都柏林
有趣的问题。由于缩略语没有标准化,因此不可能有权威的答案,也不可能有可靠的方法来获得这样的答案。我突然想到了两种方法:

  • 从JVM中获取它们
  • 在网上找到他们
  • 从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);
    
    不过,其中一些只是同一时区的名称。例如,Eire与欧洲/都柏林的规则相同。因此,如果需要,可以进行进一步的过滤。您可以使用
    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]
    
    我的方法的一个局限性是,一些时区有不止一个名称,因此有不止一个三个或四个字母的缩写。我上面的代码只捕获其中一个

    另一个限制是,像我这样选择两个日期永远不会给你所有可能的过去和未来,甚至可能错过一些我只是没有找到正确的日期。我试着选择一个日期,北半球是冬天,南半球是夏天,而另一个日期正好相反。这将涵盖目前的大多数情况,但你永远不知道是否有一个或三个时区的过渡不遵循夏季和冬季,因为我们知道它。如果你想获得更好的覆盖率,这里有一些很好的建议

    从互联网上获取它们
    当然,另一个答案是你的搜索毫无疑问已经提出的:这些名单在互联网上是公开的。例如和。正如所料,上述两份清单并不一致。例如,后者知道ADT的两种解释,前者只有一种。后一个列表给出了许多同义词缩写,从而说明了我上面的观点,即每个区域可以有多个缩写。

    有关oracle文档的,请参阅Java 8文档

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

    在实现方面,您可以使用下面的代码

    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();
    
    ZoneId losAngeles=ZoneId.of(“美国/洛杉矶”);
    柏林地区=欧洲/柏林地区;
    // 2014-02-20 12:00
    LocalDateTime dateTime=LocalDateTime.of(2014,02,20,12,0);
    //2014-02-20 12:00,欧洲/柏林(+01:00)
    ZonedDateTime berlinDateTime=ZonedDateTime.of(日期时间,柏林);
    //2014-02-20 03:00,美国/洛杉矶(-08:00)
    ZonedDateTime losAngelesDateTime=柏林时间。带ZonesMaiInstant(洛杉矶);
    int offsetInSeconds=losAngelesDateTime.getOffset().getTotalSeconds();//-28800
    //所有可用区域的集合
    设置allZoneIds=ZoneId.getAvailableZoneIds();
    
    已经提供了大量详细的信息,比如三个字母的缩写(如
    IST
    PST
    )是,你应该更喜欢长的(格式总是
    大陆/城市
    ,如
    亚洲/加尔各答
    欧洲/柏林

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

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

    它还涵盖了一个时区在过去使用缩写,但后来改为另一个,因为我正在检查所有时区的更改历史

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

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


    因此,如果您想要最新的信息,您可以定期检查IANA网站的更新。

    我编辑了您的答案的格式,但我仍然不知道这是如何解决问题的。答案很好!但不幸的是,我发现了一个无法获得所有时区的情况(不是IST-检查我的答案).但我还是保留我的投票权。你是绝对正确的,@Hugo,我已经知道这个限制,我应该从一开始就把它解释清楚。我已经编辑了。回答得很好。谢谢。我在考虑IANA时区数据库,但没有花时间去调查它是如何或是否包含缩写的。看来对于Java可能会出现缩写,包括除您自己的Java版本之外的其他Java版本,这可能是您能得到的最好的报道
    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();
    
    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);
    
    # 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