如何在 ICU4J 中获得“当前”IANA 时区缩写?
Posted
技术标签:
【中文标题】如何在 ICU4J 中获得“当前”IANA 时区缩写?【英文标题】:How can I get the "current" IANA time zone abbreviation throughout time in ICU4J? 【发布时间】:2015-10-16 01:27:03 【问题描述】:我目前正在尝试写一个suite of time zone validation programs,看看各种平台是否可以解释IANA time zone data。
我的目标输出格式包括对特定时间有效的缩写词,例如“BST”代表“英国夏令时间”,或“PST”代表“太平洋标准时间”。
在大多数平台上,这很容易 - 但奇怪的是 ICU4J 似乎无法正常工作。根据SimpleDateFormat
documentation,我应该能够使用“zzz”模式来获得我正在寻找的东西,但这似乎在很多时候都回落到 GMT+X 的“O”模式。对于某些时区,根本没有缩写。
使用纽约的简短示例:
import java.util.Date;
import java.util.Locale;
import com.ibm.icu.util.TimeZone;
import com.ibm.icu.text.SimpleDateFormat;
public class Test
public static void main(String[] args)
TimeZone zone = TimeZone.getTimeZone("America/New_York");
SimpleDateFormat format = new SimpleDateFormat("zzz", Locale.US);
format.setTimeZone(zone);
// One month before the unix epoch
System.out.println(format.format(new Date(-2678400000L))); // GMT-5
// At the unix epoch
System.out.println(format.format(new Date(0L))); // EST
(我正在使用 ICU4J 55.1 运行,无论是库存下载还是在使用 2015e 数据版本更新后。)
我不清楚 ICU4J 是从 tz 数据还是从 CLDR 获得其缩写 - 我怀疑是后者,因为 tz 数据中没有任何内容表明此处存在差异。
它似乎也受到语言环境的影响,我认为这是合理的 - 使用美国语言环境,我可以看到美国/纽约的 EST/EDT,但对于欧洲/伦敦则没有;在英国地区,我看到欧洲/伦敦的格林威治标准时间/英国夏令时,但美国/纽约没有:(
有没有办法说服 ICU4J 回退到 tz 缩写?在我非常具体的情况下,这就是我正在寻找的全部内容。
更新
感谢 RealSkeptic 的 cmets,看起来 TimeZoneNames
是一种无需格式化即可获取此数据的更简洁方法。这一切听起来很有希望——甚至还有TimeZoneNames.getTZDBInstance
:
返回一个 TimeZoneNames 实例,其中仅包含特定的短区域名称(
TimeZoneNames.NameType.SHORT_STANDARD
和TimeZoneNames.NameType.SHORT_DAYLIGHT
),与 IANA tz 数据库的区域缩写(未本地化)兼容。
这几乎正是我想要的——但在大多数情况下,这不会早于 1970 年,也不包括所有相关数据:
import static com.ibm.icu.text.TimeZoneNames.NameType.SHORT_STANDARD;
import com.ibm.icu.text.TimeZoneNames;
import com.ibm.icu.text.TimeZoneNames.NameType;
import com.ibm.icu.util.ULocale;
public class Test
public static void main(String[] args)
TimeZoneNames names = TimeZoneNames.getTZDBInstance(ULocale.ROOT);
long december1969 = -2678400000L;
// 24 hours into the Unix epoch...
long january1970 = 86400000L;
// null
System.out.println(
names.getDisplayName("America/New_York", SHORT_STANDARD, december1969));
// EST
System.out.println(
names.getDisplayName("America/New_York", SHORT_STANDARD, january1970));
// null
System.out.println(
names.getDisplayName("Europe/London", SHORT_STANDARD, december1969));
// null
System.out.println(
names.getDisplayName("Europe/London", NameType.SHORT_STANDARD, january1970));
鉴于此时几乎没有间接性 - 我正在告诉 ICU4J 我想要什么 - 我怀疑信息不可用:(
【问题讨论】:
@RealSkeptic:不确定您的意思-我正在指定格式的时区,所以应该没问题...您在阅读文档时将日历链接到什么? 对不起,我误解了你在那里问的问题。您是否尝试过使用format.setTimeZoneFormat(format.getTimeZoneFormat().setTimeZoneNames(TimeZoneNames.getTZDBInstance(ULocale.US)))
?
@RealSkeptic:不-马上就试一试!
@RealSkeptic:很有趣 - 它给出了更多个名字,但仍然不是全部,而且仍然只是从 1970 年开始。更重要的是,您的评论揭示了 TimeZoneNames
类型,这使代码更清晰。在 1970 年之前它对我没有帮助,但它仍然更好......
我认为你在这里失败了。要获取显示名称,它首先调用getMetaZoneID(String,long)
。这反过来又调用com.ibm.icu.impl.TimeZoneNamesImpl._getMetaZoneID(String,long)
,然后又调用this thing,您可以在其中看到硬编码为from
点的1970 年1 月1 日。
【参考方案1】:
追溯源代码以查看其工作原理,结果发现要找到显示名称,它从区域名称和日期中获取元区域的名称,然后从元区域和类型中获取,显示名称。
com.ibm.icu.impl.TZDBTimeZoneNames
,这是从TimeZoneNames.getTZDBInstance(ULocale)
返回的类,通过调用com.ibm.icu.impl.TimeZoneNamesImpl._getMetaZoneID(String,long)
实现getMetaZoneID(String,Long)
,它检索从给定时区名称到元区域名称的映射,然后检查日期是否介于两者之间任何这些映射中的from
和to
参数。
映射由嵌套类读取,如下所示:
for (int idx = 0; idx < zoneBundle.getSize(); idx++)
UResourceBundle mz = zoneBundle.get(idx);
String mzid = mz.getString(0);
String fromStr = "1970-01-01 00:00";
String toStr = "9999-12-31 23:59";
if (mz.getSize() == 3)
fromStr = mz.getString(1);
toStr = mz.getString(2);
long from, to;
from = parseDate(fromStr);
to = parseDate(toStr);
mzMaps.add(new MZMapEntry(mzid, from, to));
(source)
如您所见,它具有将返回的 to
和 from
值的硬编码值(尽管当元区域条目具有三个项目,其中大多数没有 - 从构建捆绑包的实际 meta zone file 可以看出 - 那些有的,也没有 1970 年 1 月之前的“开始”日期。)
因此,对于 1970 年 1 月之前的任何日期,元区域 ID 将为 null
,而显示名称也是如此。
【讨论】:
以上是关于如何在 ICU4J 中获得“当前”IANA 时区缩写?的主要内容,如果未能解决你的问题,请参考以下文章