Как я могу получить текущее сокращение часового пояса IANA во времени в ICU4J?

В настоящее время я пытаюсь написать набор программ проверки часовых поясов, чтобы узнать, интерпретируют ли различные платформы Данные часового пояса ИАНЫ.

Формат вывода, на который я нацелен, включает сокращение, действующее для определенного времени, например "BST" для "британского летнего времени" или "PST" для "тихоокеанского стандартного времени".

На большинстве платформ это просто, но, как ни странно, ICU4J не работает. Согласно SimpleDateFormat документации я должен иметь возможность использовать шаблон "zzz", чтобы получить то, что я ищу, но в большинстве случаев это похоже на шаблон "O" GMT + X. Для некоторых часовых поясов аббревиатуры вообще отсутствуют.

Краткий пример с использованием Нью-Йорка:

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 для Европы / Лондона, но ничего для Америки / Нью-Йорка :(

Есть ли способ убедить ICU4J вернуться к сокращениям tz? В моем конкретном случае это все, что я ищу.

Обновить

Благодаря комментариям RealSkeptic, похоже, 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 именно то, что я хочу - я подозреваю, что информация просто недоступна :(


person Jon Skeet    schedule 25.07.2015    source источник
comment
@RealSkeptic: Не уверен, что вы имеете в виду - я указываю часовой пояс для формата, так что все должно быть в порядке ... к чему вы привязываете календарь при чтении документов?   -  person Jon Skeet    schedule 25.07.2015
comment
Извините, я неверно истолковал ваш вопрос. Вы пробовали использовать format.setTimeZoneFormat(format.getTimeZoneFormat().setTimeZoneNames(TimeZoneNames.getTZDBInstance(ULocale.US)))?   -  person RealSkeptic    schedule 25.07.2015
comment
@RealSkeptic: Нет - попробую через минуту!   -  person Jon Skeet    schedule 25.07.2015
comment
@RealSkeptic: Интересно - это дает больше имен, но все же не все, и только с 1970 года. Что еще более важно, ваш комментарий показал тип TimeZoneNames, который делает код чище. До 1970 года мне это не помогает, но все же лучше ...   -  person Jon Skeet    schedule 25.07.2015
comment
Я думаю, у вас здесь безнадежное дело. Чтобы получить отображаемое имя, сначала вызывается getMetaZoneID(String,long). Это, в свою очередь, вызывает com.ibm.icu.impl.TimeZoneNamesImpl._getMetaZoneID(String,long), который, в свою очередь, вызывает this thing, где 1 января 1970 года жестко запрограммировано как from точка.   -  person RealSkeptic    schedule 25.07.2015
comment
@RealSkeptic: Шутка. Спасибо за внимание!   -  person Jon Skeet    schedule 25.07.2015
comment
@RealSkeptic: Похоже, вы в основном получили ответ - не стесняйтесь добавлять его как таковой, и я приму его ... очевидно, с сожалением :(   -  person Jon Skeet    schedule 25.07.2015


Ответы (1)


Прослеживая источники, чтобы увидеть, как это работает, выясняется, что для нахождения отображаемого имени оно получает имя мета-зоны из имени зоны и даты, а затем, из мета-зоны и типа, отображаемое имя. .

com.ibm.icu.impl.TZDBTimeZoneNames, который является классом, возвращаемым из TimeZoneNames.getTZDBInstance(ULocale), реализует getMetaZoneID(String,Long) путем вызова com.ibm.icu.impl.TimeZoneNamesImpl._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));
}

(источник)

Как вы можете видеть, он имеет жестко запрограммированные значения для значений to и from, которые он будет возвращать (хотя он считывает to и from из самого пакета ресурсов, когда запись мета-зоны содержит три элемента, большинство из них нет - поскольку можно увидеть в фактическом файле мета-зоны, из которых создан пакет, - а у тех, у кого он есть, также нет даты начала до января 1970 года.)

Таким образом, идентификатор мета-зоны будет null для любой даты до января 1970 года, и, в свою очередь, отображаемое имя будет таким же.

person RealSkeptic    schedule 25.07.2015