如何使用java.time API规范化时区ID?
问题
有些时区是其他时区的别名,例如撰写本文时,Europe/Skopje和Europe/Zagreb均关联到Europe/Belgrade。在Joda Time中,可通过DateTimeZone.forID(id).getID()解析这些别名,因为DateTimeZone.forID()会追踪关联返回规范化时区。那么能否使用java.time API实现类似功能?
已尝试的方案
ZoneId.of(id)会在ID不存在时抛出异常,但不会解析关联:ZoneId.of("Europe/Skopje").getId()返回"Europe/Skopje"而非"Europe/Belgrade"。ZoneId.of(id).normalized()仅规范化固定偏移时区,在此场景下无实用价值。- 所有这些时区都会被
ZoneId.getAvailableZoneIds()返回,可通过ZoneId.getRules()获取各时区的规则。规范化时区与其所有关联时区的规则一致,据此可识别Europe/Belgrade、Europe/Skopje和Europe/Zagreb等效,但无法确定哪一个是规范化时区(我也不完全确定规则相等就一定意味着时区存在关联)。
解决方案
java.time API本身并没有提供像Joda Time那样直接解析时区别名并返回规范化ID的功能——它将这些别名视为合法的独立时区ID(这类别名通常有地域或历史层面的存在意义)。不过可以通过以下方式实现类似需求:
1. 手动维护别名映射表
基于IANA时区数据库中的别名关系(比如tzdata里的backward文件),预先构建一个别名到规范ID的映射,再封装工具方法处理:
import java.time.ZoneId; import java.util.HashMap; import java.util.Map; public class ZoneIdUtils { private static final Map<String, String> ZONE_ALIAS_MAP = new HashMap<>(); static { // 示例映射,可根据实际需求补充更多别名 ZONE_ALIAS_MAP.put("Europe/Skopje", "Europe/Belgrade"); ZONE_ALIAS_MAP.put("Europe/Zagreb", "Europe/Belgrade"); } public static String getNormalizedZoneId(String zoneId) { // 先验证时区ID合法性,非法则抛出DateTimeException ZoneId.of(zoneId); return ZONE_ALIAS_MAP.getOrDefault(zoneId, zoneId); } }
2. 通过时区规则分组选择规范ID
如果不想手动维护映射表,可以通过比较ZoneRules将规则一致的时区归为一组,再为每组指定一个规范ID(比如选字典序最小的,或使用最广泛的ID):
import java.time.ZoneId; import java.time.zone.ZoneRules; import java.util.*; import java.util.stream.Collectors; public class ZoneIdNormalizer { public static String getNormalizedZoneId(String zoneId) { ZoneId targetZone = ZoneId.of(zoneId); ZoneRules targetRules = targetZone.getRules(); // 把所有时区按规则分组 Map<ZoneRules, List<String>> zoneGroups = ZoneId.getAvailableZoneIds().stream() .collect(Collectors.groupingBy(id -> ZoneId.of(id).getRules())); // 获取当前时区所在组的所有ID,选择字典序最小的作为规范ID List<String> groupIds = zoneGroups.get(targetRules); if (groupIds != null && !groupIds.isEmpty()) { Collections.sort(groupIds); return groupIds.get(0); } return zoneId; } }
注意:这种方法存在局限性——规则相等并不完全等同于别名关系,可能存在某些时区ID历史规则不同但当前规则一致的情况,此时会被错误归为一组,结果可能和Joda Time的处理不一致。
内容的提问来源于stack exchange,提问作者Malcolm Rowe
相关产品推荐
相关产品推荐

