我需要将时区选择器显示为用户控件,这看起来总是比实际更容易.在内部,我使用DateTimeZone标识符存储所有内容,因为这似乎是我需要的最精确的准确度,因为这个项目可以连接到地面媒体的实际时间.
我不想做的是提供一个包含300多个时区的选择框,我也不想用"UTC-8"(不仅丢失DST信息,而是丢失实际日期)创建伪造的时区偏移. DST落在)上.
最后,我需要一个带有包含正确TZD标识符的选项的选项,类似这样(括号#s不是必需的,仅用于潜在的最终用户插图):
<select>
<option value="America/Los_Angeles">Los Angeles [UTC-7 | DST]</option>
...
</select>
Run Code Online (Sandbox Code Playgroud)
有没有人有任何建议这个列表的指针?我用Google搜索的所有解决方案都存在问题.
我添加了一笔赏金,以防有人与我们分享更好的答案.:)
Zub*_*ir1 65
function formatOffset($offset) {
$hours = $offset / 3600;
$remainder = $offset % 3600;
$sign = $hours > 0 ? '+' : '-';
$hour = (int) abs($hours);
$minutes = (int) abs($remainder / 60);
if ($hour == 0 AND $minutes == 0) {
$sign = ' ';
}
return $sign . str_pad($hour, 2, '0', STR_PAD_LEFT) .':'. str_pad($minutes,2, '0');
}
$utc = new DateTimeZone('UTC');
$dt = new DateTime('now', $utc);
echo '<select name="userTimeZone">';
foreach(DateTimeZone::listIdentifiers() as $tz) {
$current_tz = new DateTimeZone($tz);
$offset = $current_tz->getOffset($dt);
$transition = $current_tz->getTransitions($dt->getTimestamp(), $dt->getTimestamp());
$abbr = $transition[0]['abbr'];
echo '<option value="' .$tz. '">' .$tz. ' [' .$abbr. ' '. formatOffset($offset). ']</option>';
}
echo '</select>';
Run Code Online (Sandbox Code Playgroud)
以上将输出选择菜单中的所有时区,格式如下:
<select name="userTimeZone">
<option value="America/Los_Angeles">America/Los_Angeles [PDT -7]</option>
</select>
Run Code Online (Sandbox Code Playgroud)
我的解决方案
要避免庞大的时区列表,请让用户先选择国家/地区,然后使用该信息填充时区列表.
文件populate.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Select test</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
<script type="text/javascript" charset="utf-8">
$(function(){
$("select#country").change(function(){
$.getJSON("json.php",{country: $(this).val()}, function(j){
var options = '';
for (var i = 0; i < j.length; i++) {
options += '<option value="' + j[i].optionValue + '">' + j[i].optionDisplay + '</option>';
}
$("#city").html(options);
$('#city option:first').attr('selected', 'selected');
})
})
})
</script>
</head>
<body>
<form action="#">
<label for="country">Country:</label>
<select name="country" id="country">
<option value="Portugal">Portugal</option>
<option value="United States">United States</option>
<option value="Japan">Japan</option>
</select>
<label for="city">Timezone:</label>
<select name="city" id="city">
<option value="Atlantic/Azores">Atlantic/Azores</option>
<option value="Atlantic/Madeira">Atlantic/Madeira</option>
<option value="Europe/Lisbon">Europe/Lisbon</option>
</select>
<input type="submit" name="action" value="Set TZ" />
</form>
Run Code Online (Sandbox Code Playgroud)
文件json.php
$country = $_GET['country'];
$citylist = "";
$country_list = file_get_contents("country_iso.txt"); //grab this file @ http://pastebin.com/e8gxcVHm
preg_match_all('/(.*?):'.$country.'/im', $country_list, $country_iso, PREG_PATTERN_ORDER);
$country_iso = $country_iso[1][0];
if(isset($country_iso))
{
$tz = DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $country_iso); //php 5.3 needed to use DateTimeZone::PER_COUNTRY !
foreach($tz as $city)
$citylist .= "{\"optionValue\": \"$city\", \"optionDisplay\": \"$city\"}, ";
}
$citylist = preg_replace('/, $/im', '', $citylist);
$citylist = "[".$citylist."]";
echo $citylist;
Run Code Online (Sandbox Code Playgroud)
我希望它可以帮助你:)
如果你想用zoneinfo做事,你真的没有别的选择,只能包含数百个条目,因为这只是zoneinfo的工作方式.它通常每个国家至少有一个条目,大约有200个国家(根据维基百科).
我以前做过的是使用timezone_identifiers_list()并过滤掉任何不属于标准区域的条目:
# Output option list, HTML.
$opt = '';
$regions = array('Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific');
$tzs = timezone_identifiers_list();
$optgroup = '';
sort($tzs);
foreach ($tzs as $tz) {
$z = explode('/', $tz, 2);
# timezone_identifiers_list() returns a number of
# backwards-compatibility entries. This filters them out of the
# list presented to the user.
if (count($z) != 2 || !in_array($z[0], $regions)) continue;
if ($optgroup != $z[0]) {
if ($optgroup !== '') $opt .= '</optgroup>';
$optgroup = $z[0];
$opt .= '<optgroup label="' . htmlentities($z[0]) . '">';
}
$opt .= '<option value="' . htmlentities($tz) . '" label="' . htmlentities(str_replace('_', ' ', $z[1])) . '">' . htmlentities(str_replace('_', ' ', $tz)) . '</option>';
}
if ($optgroup !== '') $opt .= '</optgroup>';
Run Code Online (Sandbox Code Playgroud)
这将创建一个包含<optgroup>元素的列表,因此列表至少在逻辑上按区域划分.
我提出了一个动态的自我更新解决方案,不需要任何查找表(选择演示):
function Timezones()
{
$result = array();
$timezones = array();
// only process geographical timezones
foreach (preg_grep('~^(?:A(?:frica|merica|ntarctica|rctic|tlantic|sia|ustralia)|Europe|Indian|Pacific)/~', timezone_identifiers_list()) as $timezone)
{
if (is_object($timezone = new DateTimeZone($timezone)) === true)
{
$id = array();
// get only the two most distant transitions
foreach (array_slice($timezone->getTransitions($_SERVER['REQUEST_TIME']), -2) as $transition)
{
// dark magic
$id[] = sprintf('%b|%+d|%u', $transition['isdst'], $transition['offset'], $transition['ts']);
}
if (count($id) > 1)
{
sort($id, SORT_NUMERIC); // sort by %b (isdst = 0) first, so that we always get the raw offset
}
$timezones[implode('|', $id)][] = $timezone->getName();
}
}
if ((is_array($timezones) === true) && (count($timezones) > 0))
{
uksort($timezones, function($a, $b) // sort offsets by -, 0, +
{
foreach (array('a', 'b') as $key)
{
$$key = explode('|', $$key);
}
return intval($a[1]) - intval($b[1]);
});
foreach ($timezones as $key => $value)
{
$zone = reset($value); // first timezone ID is our internal timezone
$result[$zone] = preg_replace(array('~^.*/([^/]+)$~', '~_~'), array('$1', ' '), $value); // "humanize" city names
if (array_key_exists(1, $offset = explode('|', $key)) === true) // "humanize" the offset
{
$offset = str_replace(' +00:00', '', sprintf('(UTC %+03d:%02u)', $offset[1] / 3600, abs($offset[1]) % 3600 / 60));
}
if (asort($result[$zone]) === true) // sort city names
{
$result[$zone] = trim(sprintf('%s %s', $offset, implode(', ', $result[$zone])));
}
}
}
return $result;
}
Run Code Online (Sandbox Code Playgroud)
有很多共享完全相同的偏移和DST的时序的时区(Europe/Dublin,Europe/Lisbon并Europe/London仅举几例),我的算法组这些区域(在数组键使用特殊的符号dst?|offset|timestamp在该组的第一个时区ID)并连接人性化的转变时区ID的最后一个(通常是城市级别)段:
Array
(
[Pacific/Midway] => (UTC -11:00) Midway, Niue, Pago Pago
[America/Adak] => (UTC -10:00) Adak
[Pacific/Fakaofo] => (UTC -10:00) Fakaofo, Honolulu, Johnston, Rarotonga, Tahiti
[Pacific/Marquesas] => (UTC -10:30) Marquesas
[America/Anchorage] => (UTC -09:00) Anchorage, Juneau, Nome, Sitka, Yakutat
[Pacific/Gambier] => (UTC -09:00) Gambier
[America/Dawson] => (UTC -08:00) Dawson, Los Angeles, Tijuana, Vancouver, Whitehorse
[America/Santa_Isabel] => (UTC -08:00) Santa Isabel
[America/Metlakatla] => (UTC -08:00) Metlakatla, Pitcairn
[America/Dawson_Creek] => (UTC -07:00) Dawson Creek, Hermosillo, Phoenix
[America/Chihuahua] => (UTC -07:00) Chihuahua, Mazatlan
[America/Boise] => (UTC -07:00) Boise, Cambridge Bay, Denver, Edmonton, Inuvik, Ojinaga, Shiprock, Yellowknife
[America/Chicago] => (UTC -06:00) Beulah, Center, Chicago, Knox, Matamoros, Menominee, New Salem, Rainy River, Rankin Inlet, Resolute, Tell City, Winnipeg
[America/Belize] => (UTC -06:00) Belize, Costa Rica, El Salvador, Galapagos, Guatemala, Managua, Regina, Swift Current, Tegucigalpa
[Pacific/Easter] => (UTC -06:00) Easter
[America/Bahia_Banderas] => (UTC -06:00) Bahia Banderas, Cancun, Merida, Mexico City, Monterrey
[America/Detroit] => (UTC -05:00) Detroit, Grand Turk, Indianapolis, Iqaluit, Louisville, Marengo, Monticello, Montreal, Nassau, New York, Nipigon, Pangnirtung, Petersburg, Thunder Bay, Toronto, Vevay, Vincennes, Winamac
[America/Atikokan] => (UTC -05:00) Atikokan, Bogota, Cayman, Guayaquil, Jamaica, Lima, Panama, Port-au-Prince
[America/Havana] => (UTC -05:00) Havana
[America/Caracas] => (UTC -05:30) Caracas
[America/Glace_Bay] => (UTC -04:00) Bermuda, Glace Bay, Goose Bay, Halifax, Moncton, Thule
[Atlantic/Stanley] => (UTC -04:00) Stanley
[America/Santiago] => (UTC -04:00) Palmer, Santiago
[America/Anguilla] => (UTC -04:00) Anguilla, Antigua, Aruba, Barbados, Blanc-Sablon, Boa Vista, Curacao, Dominica, Eirunepe, Grenada, Guadeloupe, Guyana, Kralendijk, La Paz, Lower Princes, Manaus, Marigot, Martinique, Montserrat, Port of Spain, Porto Velho, Puerto Rico, Rio Branco, Santo Domingo, St Barthelemy, St Kitts, St Lucia, St Thomas, St Vincent, Tortola
[America/Campo_Grande] => (UTC -04:00) Campo Grande, Cuiaba
[America/Asuncion] => (UTC -04:00) Asuncion
[America/St_Johns] => (UTC -04:30) St Johns
[America/Sao_Paulo] => (UTC -03:00) Sao Paulo
[America/Araguaina] => (UTC -03:00) Araguaina, Bahia, Belem, Buenos Aires, Catamarca, Cayenne, Cordoba, Fortaleza, Jujuy, La Rioja, Maceio, Mendoza, Paramaribo, Recife, Rio Gallegos, Rothera, Salta, San Juan, Santarem, Tucuman, Ushuaia
[America/Montevideo] => (UTC -03:00) Montevideo
[America/Godthab] => (UTC -03:00) Godthab
[America/Argentina/San_Luis] => (UTC -03:00) San Luis
[America/Miquelon] => (UTC -03:00) Miquelon
[America/Noronha] => (UTC -02:00) Noronha, South Georgia
[Atlantic/Cape_Verde] => (UTC -01:00) Cape Verde
[America/Scoresbysund] => (UTC -01:00) Azores, Scoresbysund
[Atlantic/Canary] => (UTC) Canary, Dublin, Faroe, Guernsey, Isle of Man, Jersey, Lisbon, London, Madeira
[Africa/Abidjan] => (UTC) Abidjan, Accra, Bamako, Banjul, Bissau, Casablanca, Conakry, Dakar, Danmarkshavn, El Aaiun, Freetown, Lome, Monrovia, Nouakchott, Ouagadougou, Reykjavik, Sao Tome, St Helena
[Africa/Algiers] => (UTC +01:00) Algiers, Bangui, Brazzaville, Douala, Kinshasa, Lagos, Libreville, Luanda, Malabo, Ndjamena, Niamey, Porto-Novo, Tunis
[Africa/Ceuta] => (UTC +01:00) Amsterdam, Andorra, Belgrade, Berlin, Bratislava, Brussels, Budapest, Ceuta, Copenhagen, Gibraltar, Ljubljana, Longyearbyen, Luxembourg, Madrid, Malta, Monaco, Oslo, Paris, Podgorica, Prague, Rome, San Marino, Sarajevo, Skopje, Stockholm, Tirane, Vaduz, Vatican, Vienna, Warsaw, Zagreb, Zurich
[Africa/Windhoek] => (UTC +01:00) Windhoek
[Asia/Damascus] => (UTC +02:00) Damascus
[Asia/Beirut] => (UTC +02:00) Beirut
[Asia/Jerusalem] => (UTC +02:00) Jerusalem
[Asia/Nicosia] => (UTC +02:00) Athens, Bucharest, Chisinau, Helsinki, Istanbul, Mariehamn, Nicosia, Riga, Sofia, Tallinn, Vilnius
[Africa/Blantyre] => (UTC +02:00) Blantyre, Bujumbura, Cairo, Gaborone, Gaza, Harare, Hebron, Johannesburg, Kigali, Lubumbashi, Lusaka, Maputo, Maseru, Mbabane, Tripoli
[Asia/Amman] => (UTC +02:00) Amman
[Africa/Addis_Ababa] => (UTC +03:00) Addis Ababa, Aden, Antananarivo, Asmara, Baghdad, Bahrain, Comoro, Dar es Salaam, Djibouti, Juba, Kaliningrad, Kampala, Khartoum, Kiev, Kuwait, Mayotte, Minsk, Mogadishu, Nairobi, Qatar, Riyadh, Simferopol, Syowa, Uzhgorod, Zaporozhye
[Asia/Tehran] => (UTC +03:30) Tehran
[Asia/Yerevan] => (UTC +04:00) Yerevan
[Asia/Dubai] => (UTC +04:00) Dubai, Mahe, Mauritius, Moscow, Muscat, Reunion, Samara, Tbilisi, Volgograd
[Asia/Baku] => (UTC +04:00) Baku
[Asia/Kabul] => (UTC +04:30) Kabul
[Antarctica/Mawson] => (UTC +05:00) Aqtau, Aqtobe, Ashgabat, Dushanbe, Karachi, Kerguelen, Maldives, Mawson, Oral, Samarkand, Tashkent
[Asia/Colombo] => (UTC +05:30) Colombo, Kolkata
[Asia/Kathmandu] => (UTC +05:45) Kathmandu
[Antarctica/Vostok] => (UTC +06:00) Almaty, Bishkek, Chagos, Dhaka, Qyzylorda, Thimphu, Vostok, Yekaterinburg
[Asia/Rangoon] => (UTC +06:30) Cocos, Rangoon
[Antarctica/Davis] => (UTC +07:00) Bangkok, Christmas, Davis, Ho Chi Minh, Hovd, Jakarta, Novokuznetsk, Novosibirsk, Omsk, Phnom Penh, Pontianak, Vientiane
[Antarctica/Casey] => (UTC +08:00) Brunei, Casey, Choibalsan, Chongqing, Harbin, Hong Kong, Kashgar, Krasnoyarsk, Kuala Lumpur, Kuching, Macau, Makassar, Manila, Perth, Shanghai, Singapore, Taipei, Ulaanbaatar, Urumqi
[Australia/Eucla] => (UTC +08:45) Eucla
[Asia/Dili] => (UTC +09:00) Dili, Irkutsk, Jayapura, Palau, Pyongyang, Seoul, Tokyo
[Australia/Adelaide] => (UTC +09:30) Adelaide, Broken Hill
[Australia/Darwin] => (UTC +09:30) Darwin
[Antarctica/DumontDUrville] => (UTC +10:00) Brisbane, Chuuk, DumontDUrville, Guam, Lindeman, Port Moresby, Saipan, Yakutsk
[Australia/Currie] => (UTC +10:00) Currie, Hobart, Melbourne, Sydney
[Australia/Lord_Howe] => (UTC +10:30) Lord Howe
[Antarctica/Macquarie] => (UTC +11:00) Efate, Guadalcanal, Kosrae, Macquarie, Noumea, Pohnpei, Sakhalin, Vladivostok
[Pacific/Norfolk] => (UTC +11:30) Norfolk
[Antarctica/McMurdo] => (UTC +12:00) Auckland, McMurdo, South Pole
[Asia/Anadyr] => (UTC +12:00) Anadyr, Fiji, Funafuti, Kamchatka, Kwajalein, Magadan, Majuro, Nauru, Tarawa, Wake, Wallis
[Pacific/Chatham] => (UTC +12:45) Chatham
[Pacific/Enderbury] => (UTC +13:00) Enderbury, Tongatapu
[Pacific/Apia] => (UTC +13:00) Apia
[Pacific/Kiritimati] => (UTC +14:00) Kiritimati
)
Run Code Online (Sandbox Code Playgroud)
当然,城市连接仍然非常长,但唯一(实际)时区列表已从414(或415,如果我们考虑非地理UTC)降至75 - 这是相当不错的IMO并且似乎反映了该列表Windows使用的"规范化"时区(也是75).
这种自动化方法存在两个大问题:
Atlantic/Canary- 而不应该有任何问题,选择一个与更大城市相关的时区ID会更有意义(比如Europe/London)array_slice($cities, 0, $maxCities)在爆炸之前使用,但这不会考虑城市维度,并且限制为4 金丝雀,都柏林,法罗,根西岛,马恩岛,泽西岛,里斯本,伦敦,马德拉将成为加那利,都柏林,法罗,格恩西岛,而不是更符合逻辑的Windows等同于都柏林,爱丁堡,里斯本,伦敦.这应该不是非常有用,但我认为我会分享 - 也许其他人可以改进它.