Java中的时区

时间:2009-11-08 00:17:19

标签: java timezone

我允许我的网络应用上的用户根据自己选择的时区安排活动。

我想向最终用户提供一个很好的时区列表,然后将其轻松转换为服务器端的java.util.TimeZone对象。

String[] TimeZone.getAvailableIds()是我可以使用的,但问题是它打印了大约585个时区ID。

向用户提供简短时区列表的最佳方式是什么(如Windows区域用于时区设置),并使用其ID轻松转换为服务器端的TimeZone对象?

9 个答案:

答案 0 :(得分:24)

时区列表非常适用于应用程序和区域设置。只有您知道哪些区域最适合您的用户。我们实际上有不同地区的不同名单。

以下是我们的美国用户列表供您参考,

    "Pacific/Midway",
    "US/Hawaii",
    "US/Alaska",
    "US/Pacific",
    "America/Tijuana",
    "US/Arizona",
    "America/Chihuahua",
    "US/Mountain",
    "America/Guatemala",
    "US/Central",
    "America/Mexico_City",
    "Canada/Saskatchewan",
    "America/Bogota",
    "US/Eastern",
    "US/East-Indiana",
    "Canada/Eastern",
    "America/Caracas",
    "America/Manaus",
    "America/Santiago",
    "Canada/Newfoundland",
    "Brazil/East",
    "America/Buenos_Aires",
    "America/Godthab",
    "America/Montevideo",
    "Atlantic/South_Georgia",
    "Atlantic/Azores",
    "Atlantic/Cape_Verde",
    "Africa/Casablanca",
    "Europe/London",
    "Europe/Berlin",
    "Europe/Belgrade",
    "Europe/Brussels",
    "Europe/Warsaw",
    "Africa/Algiers",
    "Asia/Amman",
    "Europe/Athens",
    "Asia/Beirut",
    "Africa/Cairo",
    "Africa/Harare",
    "Europe/Helsinki",
    "Asia/Jerusalem",
    "Europe/Minsk",
    "Africa/Windhoek",
    "Asia/Baghdad",
    "Asia/Kuwait",
    "Europe/Moscow",
    "Africa/Nairobi",
    "Asia/Tbilisi",
    "Asia/Tehran",
    "Asia/Muscat",
    "Asia/Baku",
    "Asia/Yerevan",
    "Asia/Kabul",
    "Asia/Yekaterinburg",
    "Asia/Karachi",
    "Asia/Calcutta",
    "Asia/Colombo",
    "Asia/Katmandu",
    "Asia/Novosibirsk",
    "Asia/Dhaka",
    "Asia/Rangoon",
    "Asia/Bangkok",
    "Asia/Krasnoyarsk",
    "Asia/Hong_Kong",
    "Asia/Irkutsk",
    "Asia/Kuala_Lumpur",
    "Australia/Perth",
    "Asia/Taipei",
    "Asia/Tokyo",
    "Asia/Seoul",
    "Asia/Yakutsk",
    "Australia/Adelaide",
    "Australia/Darwin",
    "Australia/Brisbane",
    "Australia/Sydney",
    "Pacific/Guam",
    "Australia/Hobart",
    "Asia/Vladivostok",
    "Asia/Magadan",
    "Pacific/Auckland",
    "Pacific/Fiji",
    "Pacific/Tongatapu",

答案 1 :(得分:11)

我刚刚编写了一个小型Java实用程序,它提供了Windows时区列表(Windows中时区选择对话框中的区域)及其关联的Java TimeZone对象。见https://github.com/nfergu/Java-Time-Zone-List

这基于http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml

处的CLDR映射

答案 2 :(得分:4)

您可以使用仅匹配以下正则表达式的TZ ID缩小列表

^(Africa|America|Asia|Atlantic|Australia|Europe|Indian|Pacific)/.*

答案 3 :(得分:3)

为了补充tbruyelle的答案,我添加了一些国家(例如加拿大),删除了过滤器的“/”部分,并提供了对列表进行排序的方法。

public static void main(String[] args)
{
    List<String> simplifiedTimezoneList = getTimezoneIdList();

    for (String tz : simplifiedTimezoneList)
        System.out.println(tz);
}

public static List<String> getTimezoneIdList()
{
    String[] temp = TimeZone.getAvailableIDs();
    List<String> timezoneList = new ArrayList<String>();
    List<String> simplifiedTimezoneList = new ArrayList<String>();
    for (String tz : temp)
    {
        timezoneList.add(tz);
    }
    Collections.sort(timezoneList);
    String filterList = "Canada|Mexico|Chile|Cuba|Brazil|Japan|Turkey|Mideast|Africa|America|Asia|Atlantic|Australia|Europe|Indian|Pacific";
    Pattern p = Pattern.compile("^(" + filterList + ").*");
    for (String tz : timezoneList)
    {
        Matcher m = p.matcher(tz);
        if (m.find())
        {
            simplifiedTimezoneList.add(tz);
        }
    }
    return simplifiedTimezoneList;
}

答案 4 :(得分:2)

ZoneId

TimeZone类现在已成为旧版,几年前被JSR 310中定义的现代 java.time 类所取代。具体由java.time.ZoneId取代。

大多数当前使用的时区采用Continent/Region的名称格式。请参阅可排序的list at Wikipedia

获取所有时区名称的列表。

    Set<String> zoneIds = ZoneId.getAvailableZoneIds() ;
    System.out.println( "zoneIds = " + zoneIds );

查看这些前缀的不同列表。

    zoneIds.stream().map( s -> s.split( "/" )[0] ).collect( Collectors.toSet()).stream().forEach( System.out::println );

Answer by tbruyelle中所述,缩小列表范围以呈现给用户的一种方法是过滤该Continent部分。其中,我认为最好专注于:

  • 欧洲
  • 非洲
  • 南极洲
  • 大西洋
  • 美国
  • 太平洋
  • 印度
  • 澳大利亚

…加上添加Etc/UTC

在Java代码中,按字母顺序排序。

List < String > zoneGroupNames = List.of(
        "Africa" ,
        "Antarctica" ,
        "Atlantic" ,
        "America" ,
        "Australia" ,
        "Europe" ,
        "Indian" ,
        "Pacific" ,
        "UTC"
);

区域组名称与区域名称的多重映射

为每个区域组名称构建一个Map,以收集区域ID名称。我们需要将组名(例如Europe映射到区域名列表(例如Europe/BerlinEurope/LondonEurope/Malta)。

Map < String, List < String > > mapGroupNameToZoneNames = new TreeMap <>();

将键映射到值的集合称为“多图”。现在,我们具有内置的多地图功能,其中包含与Java捆绑在一起的Map实现。呼叫Map::computeIfAbsent(请参阅this Answer)。

Set < String > zoneIdStrings = ZoneId.getAvailableZoneIds();
for ( String zoneIdString : zoneIdStrings )
{
    String groupName = zoneIdString.split( "/" )[ 0 ];
    if ( zoneGroupNames.contains( groupName ) )
    {
        mapGroupNameToZoneNames.computeIfAbsent( groupName , ( x -> new ArrayList <>() ) ).add( zoneIdString );
    } // Else skip it.
}

System.out.println( "mapGroupNameToZoneNames = " + mapGroupNameToZoneNames );

呈现给用户

向用户呈现该组列表。假设用户选择了项目#6(索引5),当前为Europe

String groupNameChosenByUser = zoneGroupNames.get( 5 ); // Europe
List < String > zoneNamesOfGroup = mapGroupNameToZoneNames.get( groupNameChosenByUser );

显示该组的区域名称列表。假设用户选择的项目#12(索引11)当前为Europe/Malta

String zoneNameChosenByUser = zoneNamesOfGroup.get( 11 );  // Malta

从该区域名称的字符串中创建一个ZoneId对象。

ZoneId zoneIdChosenByUser = ZoneId.of( zoneNameChosenByUser );

zoneIdChosenByUser.toString()=欧洲/马耳他

答案 5 :(得分:0)

您无法使用“GMT +/-小时”表示法(跳过分钟)来使用自定义时区ID列表吗?

(编辑:根据我的第一个建议,夏令时转换不是自动的。要解决此问题,您可以先要求用户选择GMT偏移,然后显示给定的(链接)时区ID列表抵消使用:

public static String[] getAvailableIDs(int rawOffset) 

这样,用户就可以在更短的列表中选择他的时区(更好地体验用户体验),并从夏令时行为中受益。)

答案 6 :(得分:0)

如果你需要精确选择列表的外观,我会使用我能找到的最好的硬编码列表(this is a good example)并确保它尽可能精确地显示和转换。

请记住,这585个时区中的每一个都具有语义含义(例如DST),用户可能希望为它们选择最佳时区。虽然我同意列表可以更短。

答案 7 :(得分:0)

我为一家我不再拥有的公司做了这个,所以无法提供代码。 Windows上的JVM附带了一个名为tzmappings的文件(查看C:\Program Files\Java\jre6\lib或类似文件),该文件将Windows时区映射到Java的基于zoneinfo的Continent / City表单。

不幸的是,tzmappings中的文字名称很糟糕,所以你需要做几分钟的制表。打开注册表并导航到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones。在这下面是机器上每个时区的关键; Windows 7大约有90个。每个键都有一个名为Display的值,它是您想要的文本名称;在tzmappings中查找密钥本身,以查找每个密钥的Java时区标识符。

答案 8 :(得分:0)

有了这么多,我不会尝试将它们变成一个选择框列表......我会把它们放在一个单独的模态对话框中的列表中(或弹出,如果必须的话),让用户滚动通过并单击他们想要的名称。他们会点击模态对话框中的链接,然后使用正确的代码填充文本字段,然后您可以将其提交给您的服务器。

更好的是,让他们在世界地图上点击他们的位置,并使用图像地图将该位置转换为适当的时区。