我允许我的网络应用上的用户根据自己选择的时区安排活动。
我想向最终用户提供一个很好的时区列表,然后将其轻松转换为服务器端的java.util.TimeZone
对象。
String[] TimeZone.getAvailableIds()
是我可以使用的,但问题是它打印了大约585个时区ID。
向用户提供简短时区列表的最佳方式是什么(如Windows区域用于时区设置),并使用其ID轻松转换为服务器端的TimeZone对象?
答案 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/Berlin
,Europe/London
和Europe/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)
有了这么多,我不会尝试将它们变成一个选择框列表......我会把它们放在一个单独的模态对话框中的列表中(或弹出,如果必须的话),让用户滚动通过并单击他们想要的名称。他们会点击模态对话框中的链接,然后使用正确的代码填充文本字段,然后您可以将其提交给您的服务器。
更好的是,让他们在世界地图上点击他们的位置,并使用图像地图将该位置转换为适当的时区。