我目前正在尝试编写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,对于欧洲/伦敦,则看不到任何东西。在英国语言环境中,我看到欧洲/伦敦的格林尼治标准时间(GMT)/英国夏令时(BST),但美国/纽约(New_York)则没有:

是否有办法说服ICU4J退回tz缩写?在我的特定情况下,这就是我想要的。

更新

感谢RealSkeptic的评论,看来TimeZoneNames是一种无需格式化即可获取此数据的更干净的方法。听起来很有希望-甚至还有TimeZoneNames.getTZDBInstance:



这几乎正​​是我想要的-但是在大多数情况下,它都不会早于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我到底想要什么-我怀疑该信息不可用:(

最佳答案

追溯源代码以了解其工作原理,结果发现要找到显示名称,它会从区域名称和日期中获取元区域的名称,然后从元区域和类型中获取显示名称。 。
com.ibm.icu.impl.TZDBTimeZoneNamesTimeZoneNames.getTZDBInstance(ULocale)返回的类,它通过调用getMetaZoneID(String,Long)来实现com.ibm.icu.impl.TimeZoneNamesImpl._getMetaZoneID(String,long)from检索从给定时区名称到元区域名称的映射,然后检查日期是否在这些映射中的任何一个的toto参数之间。

映射由嵌套类读取,如下所示:

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)

如您所见,它具有返回的fromto值的硬编码值(尽管当meta区域条目包含三项时,它会从资源包本身读取fromnull,但其中大多数都不是-如可以在构建 bundle 包的实际meta zone file中看到-那些这样做的人也没有1970年1月之前的“开始”日期。)

因此,对于1970年1月之前的任何日期,元区域ID将为ojit_code,显示名称也将为ojit_code。

10-07 22:43