Собрал нужный код, получилась такая функция, возвращающая массив для dropDownList() из Yii2 с элементами вида:
'Asia/Krasnoyarsk' => '(UTC+07:00) Красноярск'
Массив с двумерной сортировкой, сначала по сдвигу, потом по имени.
Локализация пока слабовата, может быть поэтому IntlTimeZone class пока почти не документирован.
Кстати, внутри функции устанавливается часовой пояс UTC для вычисления сдвига по времени, я не знаю область его действия. Так что используйте с осторожностью. Вообще, часть с вычислением этого сдвига мне не нравится. Если кто знает способ лучше - напишите в комментариях.
/* robust list of timezones */
function get_list_of_timezones($locale) {
date_default_timezone_set('UTC');
$identifiers = DateTimeZone::listIdentifiers();
foreach($identifiers as $i) {
// create date time zone from identifier
$dtz = new DateTimeZone($i);
// create timezone from identifier
$tz = IntlTimeZone::createTimeZone($i);
// if IntlTimeZone is unaware of timezone ID, use identifier as name, else use localized name
if ($tz->getID() === 'Etc/Unknown' or $i === 'UTC') $name = $i;
else $name = $tz->getDisplayName(false, 3, $locale);
// time offset
$offset = $dtz->getOffset(new DateTime());
$sign = ($offset < 0) ? '-' : '+';
$tzs[] = [
'code' => $i,
'name' => '(UTC' . $sign . date('H:i', abs($offset)) . ') ' . $name,
'offset' => $offset,
];
}
\yii\helpers\ArrayHelper::multisort($tzs, ['offset', 'name']);
// sort by offset
// usort($tzs, function($a, $b){
// if ($a['offset'] > $b['offset']) {
// return 1;
// }
// elseif ($a['offset'] < $b['offset']) {
// return -1;
// }
// elseif ($a['name'] > $b['name']) {
// return 1;
// }
// elseif ($a['name'] < $b['name']) {
// return -1;
// }
// return 0;
// });
return array_column($tzs, 'name', 'code');
}
?>