使用TimeZones </select>填充<select>框的最佳方法

时间:2011-08-03 04:58:42

标签: php

我需要将时区选择器显示为用户控件,这看起来总是比实际更容易。在内部,我使用DateTimeZone标识符存储所有内容,因为这似乎是我需要的最精确的准确度,因为这个项目连接到地面媒体的实际时间。

我不想做的是提供一个包含300多个时区的选择框,我也不想创建伪造的时区偏移,例如'UTC-8'(不仅会丢失DST信息,而且会丢失实际信息) DST落在的日期。)

最后,我需要一个带有包含正确TZD标识符的选项的选项,类似这样(括号内的#s不是必需的,只是为了潜在的最终用户插图):

<select>
<option value="America/Los_Angeles">Los Angeles [UTC-7 | DST]</option>
...
</select>

有没有人有建议这个清单的指示?我用Google搜索的所有解决方案都存在问题。


我添加了赏金,以防有人与我们分享更好的答案。 :)

12 个答案:

答案 0 :(得分: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>';

以上将输出选择菜单中的所有时区,格式如下:

<select name="userTimeZone">
<option value="America/Los_Angeles">America/Los_Angeles [PDT -7]</option>
</select>

答案 1 :(得分:9)

我的解决方案:

要避开庞大的时区列表,请让用户先选择国家/地区,然后使用该信息填充时区列表。

文件 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>

文件 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; 

我希望它可以帮助你:)

答案 2 :(得分:8)

如果你想用zoneinfo做事,你真的别无选择,只能包含数百个条目,因为这只是zoneinfo的工作方式。每个国家通常至少有一个条目,大约有200个国家(根据Wikipedia)。

我以前做过的是使用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>';

这会创建一个包含<optgroup>元素的列表,因此列表至少会按区域划分。

答案 3 :(得分:5)

我想出了一个动态的自我更新解决方案,不需要任何查找表(select demo):

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;
}

有很多时区共享完全相同的偏移和DST时序(Europe/DublinEurope/LisbonEurope/London,仅举几例),我的算法对这些区域进行分组(使用特殊区域)该组的第一个时区ID中的数组键dst?|offset|timestamp中的表示法,并连接时区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
)

当然,城市连接仍然非常长,但唯一(实际)时区列表已从414(或415,如果我们考虑非地理UTC)降至75 - 这是相当不错的IMO,似乎反映list of "normalized" timezones Windows uses(也是75)。

这种自动化方法存在两个大问题:

  1. 一组城市的所选时区ID是按字母顺序排列的第一个,这意味着 (UTC)Canary,Dublin,Faroe,Guernsey,Isle of Man,Jersey,Lisbon,London,马德拉 时区值为Atlantic/Canary - 虽然不应该有任何问题,但选择与更大城市相关的时区ID会更有意义(如{{ 1}})
  2. 城市的连接显然是最大的问题,其中有太多问题 - 解决这个问题的一种方法是在爆炸前使用Europe/London,但这不会考虑城市维度,并且限制4 Canary,Dublin,Faroe,Guernsey,Isle of Man,Jersey,Lisbon,London,Madeira 将成为 Canary,Dublin,Faroe,Guernsey 而不是更符合逻辑的Windows等效 都柏林,爱丁堡,里斯本,伦敦
  3. 这应该不是很有用,但我认为我会分享 - 也许其他人可以改进它。

答案 4 :(得分:4)

答案 5 :(得分:4)

在一天结束时,我认为没有一个很好的解决方案可以预先剔除太大的时区列表......而且所涉及的问题让我决定刚刚开始以一个体面的方式 - 大小的班级。我在这里发布,因为有些人对它感兴趣。希望我的解决方案也可以帮助其他人。

它的作用:

  • 让您从PHP内的整个列表中创建时区列表

  • 让您根据自己的预烘焙定义创建一个很好的列表,计算当前的UTC偏移量。

  • 允许您按国家/地区创建列表。

  • 使用相同的缩写重复时区。请注意,我没有做太多的研究,看看列表中是否有重复,我不应该删除。很明智,即使两个时区可以报告相同的缩写(例如,亚利桑那州的MST),它将进一步确定时区是否在一年中的任何时间支持DST。

  • 输出相当可配置的HTML(不进入模板路径)或JSON for Ajax或内联JavaScript。

  • 计算任何给定时区的当前时区偏移量。请注意,这不是静态的...它会在一年中发生变化。

  • 提供大量糖用于实际使用。

不做的事情:

  • 通过OptGroup将国家或大陆分开。由于我已经按UTC偏移进行了排序,这似乎会导致更多的混乱而不是缓解。

有一天我可能会添加的东西:

  • 使用模板构建HTML,而不是像这样内联。

  • 其他选择器,例如取一组国家而不是一个国家。

  • 分组,但正如我上面提到的那样,我很沮丧。

  • 代码非常潮湿。有一些次要的编码风格差异。

  • 能够指定友好名称的“首选”列表。例如,虽然'道森'是一个完全可行的候选人,被列为PDT的“城市”,因为它是PST主要群体的一部分,并观察夏令时......一个只有1000多名居民的城市不应该击败洛杉矶,旧金山,西雅图或温哥华只是因为它首先出现在词汇上。

  • 原始输出,因此没有人与简单的'toSelect'和'toJson'方法联系在一起。

一般来说,它应该符合我的需要。它正确处理了我熟悉的所有时区,以及我当前项目所需的所有时区。不幸的是,我的知识范围主要集中在美国和美国。西欧。

完美吗?可能不是。很高兴听到任何人可能对此代码有任何问题/错误/改进,因为它可能会在我的永久库中出现。如果有人认为我很高......也让我知道。这就是问题的关键在于找到准确表示时区选择器的最佳方式,这对最终用户来说实际上是有用的。

TimezoneList.php:

<?php
class TimezoneList
{
    public $timezones = array();

    private $_initialized = false;
    private $_dt_now;
    private $_utc;


    function __construct($grouped = false)
    {
        $this->_utc = new DateTimeZone('UTC');
        $this->_dt_now = new DateTime('now', $this->_utc);
    }

    // Public Static Alternate Constructors
    public static function byCountry($countryKey)
    {
        $retVal = new TimezoneList();
        $retVal->_setList($countryKey);
        return $retVal;     
    }

    public static function fromTimezones(Array $tzArr)
    {
        $retVal = new TimezoneList();
        foreach ($tzArr as $tzItem)
        {
            $retVal->timezones[] = $tzItem;
        }
        $retVal->_initialized = true;
        return $retVal;
    }

    public static function fromIdentifierList($timezoneList, $friendlyNames = NULL)
    {
        $retVal = new TimezoneList();

        if ($friendlyNames)
        {
            if (count($timezoneList) != count($friendlyNames)) throw new Exception('Array count mismatch in TimezoneBuilder::fromList');
        }

        // I'd normally use a foreach pattern, but since friendlyNames is optional, this seemed the way to go.
        for ($ii = 0; $ii < count($timezoneList); $ii++)
        {   
            $pTimezoneEx = new TimezoneExtended($timezoneList[$ii]);
            if ($friendlyNames)
            {
                $pTimezoneEx->friendlyName = $friendlyNames[$ii];
            }
            $retVal->timezones[] = $pTimezoneEx;
        }

        $retVal->_initialized = true;
        return $retVal;
    }

    // Private Statics
    // Private utility function [ Thanks to Zubair1 ]
    private static function _formatOffset($offset) 
    {
        $hours = $offset / 3600;
        $remainder = $offset % 3600;
        $sign = $hours > 0 ? '+' : '-';
        $hour = (int)abs($hours);
        $minutes = (int)abs($remainder / 60);

        $sign = (($hour == 0) && ($minutes == 0)) ? ' ' : $sign;
        return $sign.str_pad($hour, 2, '0', STR_PAD_LEFT).':'.str_pad($minutes,2, '0');
    }

    // Publics
    public function getUniqueTimezoneList($countryKey = null)
    {
        $this->_initialize();   

        $outArr = array();
        $usedTzs = array();

        foreach ($this->timezones as $timezoneEx)
        {
            if (!(in_array($timezoneEx->currentKey, $usedTzs)))
            {
                $usedTzs[] = $timezoneEx->currentKey;
                $outArr[] = $timezoneEx;
            }
        }
        usort($outArr,array('self','_orderByOffset'));

        return self::fromTimezones($outArr);
    }

    // In final code, I'll use a PHP include with output buffering as a template.
    public function toSelect($displayOffset = true, $displayCurrent = true, $selected = array(), Array $options = array())
    {
        $pOpts = array();
        $pItems = array();
        foreach ($options as $key=>$option)
        {
            $pOpts[] = ' '.$key.'="'.$option.'"';
        }
        if (!is_array($selected)) $selected = array($selected); 

        $outVal = '<select'.implode('', $pOpts).'>'."\n";

        foreach ($this->timezones as $timezoneEx)
        {
            $offset = '';
            $selectionAttr = '';
            if (in_array($timezoneEx->tzkey, $selected))
            {
                $selectionAttr = ' selected="selected"';
            }
            if ($displayOffset)
            {
                $offset = ' ['.$timezoneEx->currentAbbr.' '.self::_formatOffset($timezoneEx->currentOffset);
                if ($displayCurrent && (!($timezoneEx->observesDst))) $offset .= ' ( Does not observe DST ) ';
                $offset .= ']';
            }

            $pItems[] = "\t".'<option value="'.$timezoneEx->tzkey.'"'.$selectionAttr.'>'.$timezoneEx->friendlyName.$offset.'</option>';         
        }
        $outVal .= implode("\n", $pItems)."\n".'</select>';
        return $outVal;
    }
    public function toJson()
    {
        $outArr = array();
        foreach ($this->timezones as $timezoneEx)
        {
            $outArr[] = $timezoneEx->toShallowArray();
        }
        return json_encode($outArr);
    }

    // Privates
    private function _initialize()
    {
        if ($this->_initialized) return;
        $this->_setList();
    }
    private function _orderByOffset($a, $b)
    {
        if(  $a->currentOffset ==  $b->currentOffset ){ return 0 ; } 
        return ($a->currentOffset < $b->currentOffset) ? -1 : 1;
    }
    private function _setList($countryKey = NULL)
    {
        $this->timezones = array();
        $listType = ($countryKey) ? DateTimeZone::PER_COUNTRY : DateTimeZone::ALL;
        $tzIds = DateTimeZone::listIdentifiers($listType, $countryKey);

        foreach ($tzIds as $tzIdentifier)
        {
            $this->timezones[] = new TimezoneExtended($tzIdentifier);
        }
        $this->_initialized = true;
    }   
}

class TimezoneExtended
{
    const START_YEAR = 'January 1st';
    const MID_YEAR = 'July 1st';

    private static $_dt_startYear = NULL; // Static so that we don't have to rebuild it each time we go through.
    private static $_dt_midYear = NULL; 
    private static $_dtz_utc = NULL; 
    private static $_dt_now = NULL; 

    private $_baseObj;
    public $tzkey;
    public $friendlyName;
    public $currentKey;                 // Current Key contains the friendly Timezone Key + whether this timezone observes DST.  
                                        // This is unique across the US & Canada.  Unsure if it will be unique across other Timezones.
    public $currentAbbr;
    public $currentOffset;
    public $currentlyDst;
    public $observesDst     =   false;  // Defaults to off


    function __construct($tzKey)
    {
        if (empty(self::$_dtz_utc)) self::$_dtz_utc = new DateTimeZone('UTC');
        if (empty(self::$_dtz_now)) self::$_dt_now = new DateTime('now', self::$_dtz_utc);

        if (empty(self::$_dt_startYear)) self::$_dt_startYear = new DateTime(self::START_YEAR, self::$_dtz_utc);
        if (empty(self::$_dt_midYear)) self::$_dt_midYear = new DateTime(self::MID_YEAR, self::$_dtz_utc);

        $this->tzkey = $tzKey;
        $this->_baseObj = new DateTimeZone($tzKey);
        if ($this->_baseObj == NULL) throw new Exception('Invalid Timezone Key');

        foreach ($this->_baseObj->getTransitions(self::$_dt_startYear->getTimestamp()) as $transition)
        {
            if ($transition['isdst']) $this->observesDst = true;
        }
        foreach ($this->_baseObj->getTransitions(self::$_dt_midYear->getTimestamp()) as $transition)
        {
            if ($transition['isdst']) $this->observesDst = true;
        }
        $this->friendlyName =str_replace('_',' ',array_pop(explode('/',$tzKey)));
        $pTransition = $this->_baseObj->getTransitions(self::$_dt_now->getTimestamp());
        $this->currentAbbr = $pTransition[0]['abbr']; // With a Timestamp, we should only get one transition.
        $this->currentlyDst = $pTransition[0]['isdst'];
        $this->currentKey = $this->currentAbbr.'_'.$this->observesDst;
        $this->currentOffset = $this->_baseObj->getOffset(self::$_dt_now);
    }
    public function toShallowArray()
    {
        $outArr = array(
            'tzkey'=>$this->tzkey,
            'friendlyName'=>$this->friendlyName,
            'currentOffset'=>$this->currentOffset/3600,
            'observesDst'=>$this->observesDst,
            'currentlyDst'=>$this->currentlyDst,
                    'currentAbbr'=>$this->currentAbbr
        );
        return $outArr;
    }
}

呼。这是用法示例(timezones.php):

<?php
include_once 'TimezoneList.php';

/* Example 1:  Get Select Box by Country Code */
$tzl = TimezoneList::byCountry('US');
$tzl = $tzl->getUniqueTimezoneList();
echo $tzl->toSelect(true,true,'America/Los_Angeles');

echo "\n".'<br />'."\n";

/* Example 2:  Get a list by country code, output as JSON for AJAX (or similar uses) */
$_REQUEST['country_code'] = 'US'; // Hack for quick usage.
$tzl_ajax = TimezoneList::byCountry($_REQUEST['country_code']);
$tzl_ajax = $tzl_ajax->getUniqueTimezoneList();
echo '<script type="text/javascript">'."\n";
echo 'var foo = '.$tzl_ajax->toJson().';';
echo "\n".'</script>';  

echo "\n".'<br />'."\n";

/* Example 3:  Get Select Box from a list of TZDs + friendly names */
$tzl2 = TimezoneList::fromIdentifierList(
    array('America/Los_Angeles','America/Boise','America/Phoenix','America/Chicago','America/New_York'),
    array('Pacific','Mountain','Mountain (Arizona)','Central','Eastern')
);
// Example shows setting extra properties on the <SELECT>.
echo $tzl2->toSelect(true,false,'America/Los_Angeles', 
    array('style'=>'font-size:15px; border:1px solid #ccc; padding:4px', 'id'=>'timezone_list', 'class'=>'standard-list', 'name'=>'timezone')
);

echo "\n".'<br />'."\n";    

/* Example 4:  Get a raw list of timezones */
$tzl3 = new TimezoneList(true);
$tzl3 = $tzl3->getUniqueTimezoneList();
echo $tzl3->toSelect(true,false,'America/Los_Angeles');

这是timezones.php中示例的输出代码:

<select> 
    <option value="Pacific/Honolulu">Honolulu [HST -10:00 ( Does not observe DST ) ]</option> 
    <option value="America/Adak">Adak [HADT -09:00]</option> 
    <option value="America/Anchorage">Anchorage [AKDT -08:00]</option> 
    <option value="America/Phoenix">Phoenix [MST -07:00 ( Does not observe DST ) ]</option> 
    <option value="America/Los_Angeles" selected="selected">Los Angeles [PDT -07:00]</option> 
    <option value="America/Boise">Boise [MDT -06:00]</option> 
    <option value="America/Chicago">Chicago [CDT -05:00]</option> 
    <option value="America/Detroit">Detroit [EDT -04:00]</option> 
</select> 
<br /> 
<script type="text/javascript"> 
var foo = [{"tzkey":"Pacific\/Honolulu","friendlyName":"Honolulu","currentOffset":-10,"observesDst":false,"currentlyDst":false,"currentAbbr":"HST"},{"tzkey":"America\/Adak","friendlyName":"Adak","currentOffset":-9,"observesDst":true,"currentlyDst":true,"currentAbbr":"HADT"},{"tzkey":"America\/Anchorage","friendlyName":"Anchorage","currentOffset":-8,"observesDst":true,"currentlyDst":true,"currentAbbr":"AKDT"},{"tzkey":"America\/Phoenix","friendlyName":"Phoenix","currentOffset":-7,"observesDst":false,"currentlyDst":false,"currentAbbr":"MST"},{"tzkey":"America\/Los_Angeles","friendlyName":"Los Angeles","currentOffset":-7,"observesDst":true,"currentlyDst":true,"currentAbbr":"PDT"},{"tzkey":"America\/Boise","friendlyName":"Boise","currentOffset":-6,"observesDst":true,"currentlyDst":true,"currentAbbr":"MDT"},{"tzkey":"America\/Chicago","friendlyName":"Chicago","currentOffset":-5,"observesDst":true,"currentlyDst":true,"currentAbbr":"CDT"},{"tzkey":"America\/Detroit","friendlyName":"Detroit","currentOffset":-4,"observesDst":true,"currentlyDst":true,"currentAbbr":"EDT"}];
</script> 
<br /> 
<select style="font-size:15px; border:1px solid #ccc; padding:4px" id="timezone_list" class="standard-list" name="timezone"> 
    <option value="America/Los_Angeles" selected="selected">Pacific [PDT -07:00]</option> 
    <option value="America/Boise">Mountain [MDT -06:00]</option> 
    <option value="America/Phoenix">Mountain (Arizona) [MST -07:00]</option> 
    <option value="America/Chicago">Central [CDT -05:00]</option> 
    <option value="America/New_York">Eastern [EDT -04:00]</option> 
</select> 
<br /> 
<select> 
    <option value="Pacific/Midway">Midway [SST -11:00]</option> 
    <option value="Pacific/Niue">Niue [NUT -11:00]</option> 
    <option value="Pacific/Apia">Apia [WST -11:00]</option> 
    <option value="Pacific/Tahiti">Tahiti [TAHT -10:00]</option> 
    <option value="Pacific/Honolulu">Honolulu [HST -10:00]</option> 
    <option value="Pacific/Rarotonga">Rarotonga [CKT -10:00]</option> 
    <option value="Pacific/Fakaofo">Fakaofo [TKT -10:00]</option> 
    <option value="Pacific/Marquesas">Marquesas [MART -09:30]</option> 
    <option value="America/Adak">Adak [HADT -09:00]</option> 
    <option value="Pacific/Gambier">Gambier [GAMT -09:00]</option> 
    <option value="America/Anchorage">Anchorage [AKDT -08:00]</option> 
    <option value="Pacific/Pitcairn">Pitcairn [PST -08:00]</option> 
    <option value="America/Dawson_Creek">Dawson Creek [MST -07:00]</option> 
    <option value="America/Dawson">Dawson [PDT -07:00]</option> 
    <option value="America/Belize">Belize [CST -06:00]</option> 
    <option value="America/Boise">Boise [MDT -06:00]</option> 
    <option value="Pacific/Easter">Easter [EAST -06:00]</option> 
    <option value="Pacific/Galapagos">Galapagos [GALT -06:00]</option> 
    <option value="America/Resolute">Resolute [CDT -05:00]</option> 
    <option value="America/Cancun">Cancun [CDT -05:00]</option> 
    <option value="America/Guayaquil">Guayaquil [ECT -05:00]</option> 
    <option value="America/Lima">Lima [PET -05:00]</option> 
    <option value="America/Bogota">Bogota [COT -05:00]</option> 
    <option value="America/Atikokan">Atikokan [EST -05:00]</option> 
    <option value="America/Caracas">Caracas [VET -04:30]</option> 
    <option value="America/Guyana">Guyana [GYT -04:00]</option> 
    <option value="America/Campo_Grande">Campo Grande [AMT -04:00]</option> 
    <option value="America/La_Paz">La Paz [BOT -04:00]</option> 
    <option value="America/Anguilla">Anguilla [AST -04:00]</option> 
    <option value="Atlantic/Stanley">Stanley [FKT -04:00]</option> 
    <option value="America/Detroit">Detroit [EDT -04:00]</option> 
    <option value="America/Boa_Vista">Boa Vista [AMT -04:00]</option> 
    <option value="America/Santiago">Santiago [CLT -04:00]</option> 
    <option value="America/Asuncion">Asuncion [PYT -04:00]</option> 
    <option value="Antarctica/Rothera">Rothera [ROTT -03:00]</option> 
    <option value="America/Paramaribo">Paramaribo [SRT -03:00]</option> 
    <option value="America/Sao_Paulo">Sao Paulo [BRT -03:00]</option> 
    <option value="America/Argentina/Buenos_Aires">Buenos Aires [ART -03:00]</option> 
    <option value="America/Cayenne">Cayenne [GFT -03:00]</option> 
    <option value="America/Glace_Bay">Glace Bay [ADT -03:00]</option> 
    <option value="America/Argentina/San_Luis">San Luis [WARST -03:00]</option> 
    <option value="America/Araguaina">Araguaina [BRT -03:00]</option> 
    <option value="America/Montevideo">Montevideo [UYT -03:00]</option> 
    <option value="America/St_Johns">St Johns [NDT -02:30]</option> 
    <option value="America/Miquelon">Miquelon [PMDT -02:00]</option> 
    <option value="America/Noronha">Noronha [FNT -02:00]</option> 
    <option value="America/Godthab">Godthab [WGST -02:00]</option> 
    <option value="Atlantic/Cape_Verde">Cape Verde [CVT -01:00]</option> 
    <option value="Atlantic/Azores">Azores [AZOST  00:00]</option> 
    <option value="America/Scoresbysund">Scoresbysund [EGST  00:00]</option> 
    <option value="UTC">UTC [UTC  00:00]</option> 
    <option value="Africa/Abidjan">Abidjan [GMT  00:00]</option> 
    <option value="Africa/Casablanca">Casablanca [WET  00:00]</option> 
    <option value="Africa/Bangui">Bangui [WAT +01:00]</option> 
    <option value="Europe/Guernsey">Guernsey [BST +01:00]</option> 
    <option value="Europe/Dublin">Dublin [IST +01:00]</option> 
    <option value="Africa/Algiers">Algiers [CET +01:00]</option> 
    <option value="Atlantic/Canary">Canary [WEST +01:00]</option> 
    <option value="Africa/Windhoek">Windhoek [WAT +01:00]</option> 
    <option value="Africa/Johannesburg">Johannesburg [SAST +02:00]</option> 
    <option value="Africa/Blantyre">Blantyre [CAT +02:00]</option> 
    <option value="Africa/Tripoli">Tripoli [EET +02:00]</option> 
    <option value="Africa/Ceuta">Ceuta [CEST +02:00]</option> 
    <option value="Asia/Jerusalem">Jerusalem [IDT +03:00]</option> 
    <option value="Africa/Addis_Ababa">Addis Ababa [EAT +03:00]</option> 
    <option value="Africa/Cairo">Cairo [EEST +03:00]</option> 
    <option value="Antarctica/Syowa">Syowa [SYOT +03:00]</option> 
    <option value="Europe/Volgograd">Volgograd [VOLST +04:00]</option> 
    <option value="Europe/Samara">Samara [SAMST +04:00]</option> 
    <option value="Asia/Tbilisi">Tbilisi [GET +04:00]</option> 
    <option value="Europe/Moscow">Moscow [MSD +04:00]</option> 
    <option value="Asia/Dubai">Dubai [GST +04:00]</option> 
    <option value="Indian/Mauritius">Mauritius [MUT +04:00]</option> 
    <option value="Indian/Reunion">Reunion [RET +04:00]</option> 
    <option value="Indian/Mahe">Mahe [SCT +04:00]</option> 
    <option value="Asia/Tehran">Tehran [IRDT +04:30]</option> 
    <option value="Asia/Kabul">Kabul [AFT +04:30]</option> 
    <option value="Asia/Aqtau">Aqtau [AQTT +05:00]</option> 
    <option value="Asia/Ashgabat">Ashgabat [TMT +05:00]</option> 
    <option value="Asia/Oral">Oral [ORAT +05:00]</option> 
    <option value="Asia/Yerevan">Yerevan [AMST +05:00]</option> 
    <option value="Asia/Baku">Baku [AZST +05:00]</option> 
    <option value="Indian/Kerguelen">Kerguelen [TFT +05:00]</option> 
    <option value="Indian/Maldives">Maldives [MVT +05:00]</option> 
    <option value="Asia/Karachi">Karachi [PKT +05:00]</option> 
    <option value="Asia/Dushanbe">Dushanbe [TJT +05:00]</option> 
    <option value="Asia/Samarkand">Samarkand [UZT +05:00]</option> 
    <option value="Antarctica/Mawson">Mawson [MAWT +05:00]</option> 
    <option value="Asia/Colombo">Colombo [IST +05:30]</option> 
    <option value="Asia/Kathmandu">Kathmandu [NPT +05:45]</option> 
    <option value="Indian/Chagos">Chagos [IOT +06:00]</option> 
    <option value="Asia/Bishkek">Bishkek [KGT +06:00]</option> 
    <option value="Asia/Almaty">Almaty [ALMT +06:00]</option> 
    <option value="Antarctica/Vostok">Vostok [VOST +06:00]</option> 
    <option value="Asia/Yekaterinburg">Yekaterinburg [YEKST +06:00]</option> 
    <option value="Asia/Dhaka">Dhaka [BDT +06:00]</option> 
    <option value="Asia/Thimphu">Thimphu [BTT +06:00]</option> 
    <option value="Asia/Qyzylorda">Qyzylorda [QYZT +06:00]</option> 
    <option value="Indian/Cocos">Cocos [CCT +06:30]</option> 
    <option value="Asia/Rangoon">Rangoon [MMT +06:30]</option> 
    <option value="Asia/Jakarta">Jakarta [WIT +07:00]</option> 
    <option value="Asia/Hovd">Hovd [HOVT +07:00]</option> 
    <option value="Antarctica/Davis">Davis [DAVT +07:00]</option> 
    <option value="Asia/Bangkok">Bangkok [ICT +07:00]</option> 
    <option value="Indian/Christmas">Christmas [CXT +07:00]</option> 
    <option value="Asia/Omsk">Omsk [OMSST +07:00]</option> 
    <option value="Asia/Novokuznetsk">Novokuznetsk [NOVST +07:00]</option> 
    <option value="Asia/Choibalsan">Choibalsan [CHOT +08:00]</option> 
    <option value="Asia/Ulaanbaatar">Ulaanbaatar [ULAT +08:00]</option> 
    <option value="Asia/Brunei">Brunei [BNT +08:00]</option> 
    <option value="Antarctica/Casey">Casey [WST +08:00]</option> 
    <option value="Asia/Singapore">Singapore [SGT +08:00]</option> 
    <option value="Asia/Manila">Manila [PHT +08:00]</option> 
    <option value="Asia/Hong_Kong">Hong Kong [HKT +08:00]</option> 
    <option value="Asia/Krasnoyarsk">Krasnoyarsk [KRAST +08:00]</option> 
    <option value="Asia/Makassar">Makassar [CIT +08:00]</option> 
    <option value="Asia/Kuala_Lumpur">Kuala Lumpur [MYT +08:00]</option> 
    <option value="Australia/Eucla">Eucla [CWST +08:45]</option> 
    <option value="Pacific/Palau">Palau [PWT +09:00]</option> 
    <option value="Asia/Tokyo">Tokyo [JST +09:00]</option> 
    <option value="Asia/Dili">Dili [TLT +09:00]</option> 
    <option value="Asia/Jayapura">Jayapura [EIT +09:00]</option> 
    <option value="Asia/Pyongyang">Pyongyang [KST +09:00]</option> 
    <option value="Asia/Irkutsk">Irkutsk [IRKST +09:00]</option> 
    <option value="Australia/Adelaide">Adelaide [CST +09:30]</option> 
    <option value="Asia/Yakutsk">Yakutsk [YAKST +10:00]</option> 
    <option value="Australia/Currie">Currie [EST +10:00]</option> 
    <option value="Pacific/Port_Moresby">Port Moresby [PGT +10:00]</option> 
    <option value="Pacific/Guam">Guam [ChST +10:00]</option> 
    <option value="Pacific/Truk">Truk [TRUT +10:00]</option> 
    <option value="Antarctica/DumontDUrville">DumontDUrville [DDUT +10:00]</option> 
    <option value="Australia/Lord_Howe">Lord Howe [LHST +10:30]</option> 
    <option value="Pacific/Ponape">Ponape [PONT +11:00]</option> 
    <option value="Pacific/Kosrae">Kosrae [KOST +11:00]</option> 
    <option value="Antarctica/Macquarie">Macquarie [MIST +11:00]</option> 
    <option value="Pacific/Noumea">Noumea [NCT +11:00]</option> 
    <option value="Pacific/Efate">Efate [VUT +11:00]</option> 
    <option value="Pacific/Guadalcanal">Guadalcanal [SBT +11:00]</option> 
    <option value="Asia/Sakhalin">Sakhalin [SAKST +11:00]</option> 
    <option value="Asia/Vladivostok">Vladivostok [VLAST +11:00]</option> 
    <option value="Pacific/Norfolk">Norfolk [NFT +11:30]</option> 
    <option value="Asia/Kamchatka">Kamchatka [PETST +12:00]</option> 
    <option value="Pacific/Tarawa">Tarawa [GILT +12:00]</option> 
    <option value="Asia/Magadan">Magadan [MAGST +12:00]</option> 
    <option value="Pacific/Wallis">Wallis [WFT +12:00]</option> 
    <option value="Pacific/Kwajalein">Kwajalein [MHT +12:00]</option> 
    <option value="Pacific/Funafuti">Funafuti [TVT +12:00]</option> 
    <option value="Pacific/Nauru">Nauru [NRT +12:00]</option> 
    <option value="Asia/Anadyr">Anadyr [ANAST +12:00]</option> 
    <option value="Antarctica/McMurdo">McMurdo [NZST +12:00]</option> 
    <option value="Pacific/Wake">Wake [WAKT +12:00]</option> 
    <option value="Pacific/Fiji">Fiji [FJT +12:00]</option> 
    <option value="Pacific/Chatham">Chatham [CHAST +12:45]</option> 
    <option value="Pacific/Enderbury">Enderbury [PHOT +13:00]</option> 
    <option value="Pacific/Tongatapu">Tongatapu [TOT +13:00]</option> 
    <option value="Pacific/Kiritimati">Kiritimati [LINT +14:00]</option> 
</select>

答案 6 :(得分:3)

我可以想到几个选择。

首先填充整个选择列表,并使用jQuery插件(如Chosen)填充整个列表。这可能很糟糕,因为您的用户可能甚至不知道所有时区。

第二个选项是使用JS在隐藏的输入字段中获取浏览器的本地时间,并将其与用户IP的知识相结合,以尝试透明地推断用户位置。您甚至可以在地图上显示他们的假定位置,并询问他们是否有正确的时间。

第三种选择是实际询问用户他们住在哪里。国家和邮政编码应该足以为大多数用户获取时区。在边缘情况下,您可以要求用户澄清。

就个人而言,我可能会将第一个和第二个解决方案结合起来,使选择列表显示您认为用户最接近的区域。

答案 7 :(得分:3)

也许我们可以使用像这里提到的预生成列表:http://www.ultramegatech.com/blog/2009/04/working-with-time-zones-in-php

答案 8 :(得分:2)

在思考(并测试)了很多关于这个问题之后,我得出的结论是,你不能使用时区短名单来解决时区规则是动态的简单事实(超出你的预期 - 4个时区将会他们的规则在本月有所改变)和群组/连接/缩写是静态的。

以下是我决定坚持使用的方法的代码:

function Timezones($country = null, $continent = null)
{
    $result = array();

    if (is_array($timezones = DateTimeZone::listIdentifiers()) === true)
    {
        $timestamp = strtotime('-6 months', time());

        if ((strlen($country) == 2) && (defined('DateTimeZone::PER_COUNTRY') === true))
        {
            $timezones = DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $country);
        }

        foreach (preg_grep('~' . preg_quote($continent, '~') . '/~i', $timezones) as $id)
        {
            $timezone = new DateTimeZone($id);

            if (is_array($transitions = $timezone->getTransitions()) === true)
            {
                while ((isset($result[$id]) !== true) && (is_null($transition = array_shift($transitions)) !== true))
                {
                    $result[$id] = (($transition['isdst'] !== true) && ($transition['ts'] >= $timestamp)) ? $transition['offset'] : null;
                }
            }
        }

        if (array_multisort($result, SORT_NUMERIC, preg_replace('~^[^/]+/~', '', array_keys($result)), SORT_REGULAR, $result) === true)
        {
            foreach ($result as $key => $value)
            {
                $result[$key] = sprintf('(GMT %+03d:%02u) %s', $value / 3600, abs($value) % 3600 / 60, ltrim(strstr($key, '/'), '/'));
            }
        }
    }

    return str_replace(array(' +00:00', '_', '/'), array('', ' ', ' - '), $result);
}
  • 支持同时按国家和大陆进行过滤(如美国/美国或美国/太平洋地区)
  • 为每个时区动态计算标准(原始)偏移量
  • 时区首先按其偏移量排序,然后按其位置排序
  • 将时区标识符转换为人类可读的表示

http://www.ideone.com/VYWtwhttp://jsfiddle.net/hSxa8/embedded/result/进行演示。

答案 9 :(得分:1)

this question的答案中提供了一个列表/参考。 它提供了88个独特的时区,构成您的选择列表。

答案 10 :(得分:1)

这已经得到了很长时间的回答但是,我对任何迫使用户通过php知道的所有TZ条目的答案都不满意。相反,我创建了一个更简洁的列表,将东部时间映射到America / New_York,并包含像亚利桑那州这样奇怪位置的特殊条目。

Github条目:https://github.com/ryanzor/timezone-dropdown

仅选择器演示:http://lifesnow.com/time-zone-dropdown/

<?php

/**
  * 
  * This get's the timezone offset based on the olson code.
  * In this code it is used to find the offset between the given olson code and UTC, but can be used to convert other differences
  * 
  * @param string $remote_tz TZ string
  * @param string $origin_tz TZ string, defaults to UTC
  * @return int offset in seconds
  */

 function ln_get_timezone_offset($remote_tz, $origin_tz = 'UTC') {
    $origin_dtz = new DateTimeZone($origin_tz);
    $remote_dtz = new DateTimeZone($remote_tz);
    $origin_dt = new DateTime("now", $origin_dtz);
    $remote_dt = new DateTime("now", $remote_dtz);
    $offset = $remote_dtz->getOffset($remote_dt) - $origin_dtz->getOffset($origin_dt);
    return $offset;
}

/**
 * Converts a timezone difference to be displayed as GMT +/-
 * 
 * @param string $timezone TZ time
 * @return string text with GMT
 */

function ln_get_timezone_offset_text($timezone){
    $time = ln_get_timezone_offset($timezone);

    $minutesOffset = $time/60;
    $hours = floor(($minutesOffset)/60);
    $minutes = abs($minutesOffset%60);
    $minutesFormatted = sprintf('%02d', $minutes);
    $plus = '';
    if($time >= 0){
        $plus = '+';
    }
    $GMToff = 'GMT '.$plus.$hours.':'.$minutesFormatted;
    return $GMToff;
}


/**
 * This is for formatting how the timezone option displays.
 * It can be converted to include current time, not include gmt or anything like that.
 * 
 * @param string $timezone TZ time
 * @param string $text format select box option
 */
function ln_display_timezone_option($timezone, $text){
    ?>
    <option value="<?php echo $timezone; ?>"><?php echo '('.ln_get_timezone_offset_text($timezone).') '.$text; ?></option>
    <?php
}

/**
 *  The concise list of timezones.  This generates the html wherever it is called
 */

function ln_display_timezone_selector(){

    ?>
    <select name="timezoneSelectDropdown">
        <?php
        ln_display_timezone_option('Pacific/Auckland', 'International Date Line West');
        ln_display_timezone_option('Pacific/Midway', 'Midway Island, Samoa');
        ln_display_timezone_option('US/Hawaii', 'Hawaii');
        ln_display_timezone_option('US/Alaska', 'Alaska');
        ln_display_timezone_option('US/Pacific', 'Pacific Time (US & Canada)');
        ln_display_timezone_option('America/Tijuana', 'Tijuana, Baja California');
        ln_display_timezone_option('America/Phoenix', 'Arizona');
        ln_display_timezone_option('America/Chihuahua', 'Chihuahua, La Paz, Mazatlan');
        ln_display_timezone_option('US/Mountain', 'Mountain Time (US & Canada)');
        ln_display_timezone_option('America/Cancun', 'Central America');
        ln_display_timezone_option('US/Central', 'Central Time (US & Canada)');
        ln_display_timezone_option('America/Mexico_City', 'Guadalajara, Mexico City, Monterrey');
        ln_display_timezone_option('Canada/Saskatchewan', 'Saskatchewan');
        ln_display_timezone_option('America/Lima', 'Bogota, Lima, Quito, Rio Branco');
        ln_display_timezone_option('US/Eastern', 'Eastern Time (US & Canada)');
        ln_display_timezone_option('US/East-Indiana', 'Indiana (East)');
        ln_display_timezone_option('Canada/Atlantic', 'Atlantic Time (Canada)');
        ln_display_timezone_option('America/Caracas', 'Caracas, La Paz');
        ln_display_timezone_option('America/Manaus', 'Manaus');
        ln_display_timezone_option('America/Santiago', 'Santiago');
        ln_display_timezone_option('Canada/Newfoundland', 'Newfoundland');
        ln_display_timezone_option('America/Sao_Paulo', 'Brasilia');
        ln_display_timezone_option('America/Argentina/Buenos_Aires', 'Buenos Aires, Georgetown');
        ln_display_timezone_option('America/Godthab', 'Greenland');
        ln_display_timezone_option('America/Montevideo', 'Montevideo');
        ln_display_timezone_option('Atlantic/South_Georgia', 'Mid-Atlantic');
        ln_display_timezone_option('Atlantic/Cape_Verde', 'Cape Verde Is.');
        ln_display_timezone_option('Atlantic/Azores', 'Azores');
        ln_display_timezone_option('Africa/Casablanca', 'Casablanca, Monrovia, Reykjavik');
        ln_display_timezone_option('UTC', 'Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London');
        ln_display_timezone_option('Europe/Amsterdam', 'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna');
        ln_display_timezone_option('Europe/Belgrade', 'Belgrade, Bratislava, Budapest, Ljubljana, Prague');
        ln_display_timezone_option('Europe/Brussels', 'Brussels, Copenhagen, Madrid, Paris');
        ln_display_timezone_option('Europe/Sarajevo', 'Sarajevo, Skopje, Warsaw, Zagreb');
        ln_display_timezone_option('Africa/Windhoek', 'West Central Africa');
        ln_display_timezone_option('Asia/Amman', 'Amman');
        ln_display_timezone_option('Europe/Athens', 'Athens, Bucharest, Istanbul');
        ln_display_timezone_option('Asia/Beirut', 'Beirut');
        ln_display_timezone_option('Africa/Cairo', 'Cairo');
        ln_display_timezone_option('Africa/Harare', 'Harare, Pretoria');
        ln_display_timezone_option('Europe/Helsinki', 'Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius');
        ln_display_timezone_option('Asia/Jerusalem', 'Jerusalem');
        ln_display_timezone_option('Europe/Minsk', 'Minsk');
        ln_display_timezone_option('Africa/Windhoek', 'Windhoek');
        ln_display_timezone_option('Asia/Kuwait', 'Kuwait, Riyadh, Baghdad');
        ln_display_timezone_option('Europe/Moscow', 'Moscow, St. Petersburg, Volgograd');
        ln_display_timezone_option('Africa/Nairobi', 'Nairobi');
        ln_display_timezone_option('Asia/Tbilisi', 'Tbilisi');
        ln_display_timezone_option('Asia/Tehran', 'Tehran');
        ln_display_timezone_option('Asia/Muscat', 'Abu Dhabi, Muscat');
        ln_display_timezone_option('Asia/Baku', 'Baku');
        ln_display_timezone_option('Asia/Yerevan', 'Yerevan');
        ln_display_timezone_option('Asia/Kabul', 'Kabul');
        ln_display_timezone_option('Asia/Yekaterinburg', 'Yekaterinburg');
        ln_display_timezone_option('Asia/Karachi', 'Islamabad, Karachi, Tashkent');
        ln_display_timezone_option('Asia/Kolkata', 'Sri Jayawardenepura');
        ln_display_timezone_option('Asia/Kolkata', 'Chennai, Kolkata, Mumbai, New Delhi');
        ln_display_timezone_option('Asia/Kathmandu', 'Kathmandu');
        ln_display_timezone_option('Asia/Almaty', 'Almaty, Novosibirsk');
        ln_display_timezone_option('Asia/Dhaka', 'Astana, Dhaka');
        ln_display_timezone_option('Asia/Rangoon', 'Yangon (Rangoon)');
        ln_display_timezone_option('Asia/Bangkok', 'Bangkok, Hanoi, Jakarta');
        ln_display_timezone_option('Asia/Krasnoyarsk', 'Krasnoyarsk');
        ln_display_timezone_option('Asia/Shanghai', 'Beijing, Chongqing, Hong Kong, Urumqi');
        ln_display_timezone_option('Asia/Singapore', 'Kuala Lumpur, Singapore');
        ln_display_timezone_option('Asia/Irkutsk', 'Irkutsk, Ulaan Bataar');
        ln_display_timezone_option('Australia/Perth', 'Perth');
        ln_display_timezone_option('Asia/Taipei', 'Taipei');
        ln_display_timezone_option('Asia/Tokyo', 'Osaka, Sapporo, Tokyo');
        ln_display_timezone_option('Asia/Seoul', 'Seoul');
        ln_display_timezone_option('Asia/Yakutsk', 'Yakutsk');
        ln_display_timezone_option('Australia/Adelaide', 'Adelaide');
        ln_display_timezone_option('Australia/Darwin', 'Darwin');
        ln_display_timezone_option('Australia/Brisbane', 'Brisbane');
        ln_display_timezone_option('Australia/Sydney', 'Canberra, Melbourne, Sydney');
        ln_display_timezone_option('Australia/Hobart', 'Hobart');
        ln_display_timezone_option('Pacific/Guam', 'Guam, Port Moresby');
        ln_display_timezone_option('Asia/Vladivostok', 'Vladivostok');
        ln_display_timezone_option('Asia/Magadan', 'Magadan, Solomon Is., New Caledonia');
        ln_display_timezone_option('Pacific/Auckland', 'Auckland, Wellington');
        ln_display_timezone_option('Pacific/Fiji', 'Fiji, Kamchatka, Marshall Is.');
        ln_display_timezone_option('Pacific/Tongatapu', 'Nuku\'alofa');

        ?>
    </select>
    <?php
}
?>

答案 11 :(得分:0)

类似于accepted answer,但更短,并且还显示了每个时区的当前时间:

foreach(timezone_identifiers_list() as $tz)
{
    $current_dt = new DateTime('now', new DateTimeZone($tz));

    echo '<option value=' . $tz . '>'
            . str_replace('_', ' ', $tz) . ' - ' // Some timezones contain _
            . $current_dt->format('\G\M\TP (H:i)') // Add the time in $tz
        . '</option>';
}

输出类似:Europe/Brussels - GMT+02:00 (23:57)
任何valid format(格林尼治标准时间== UTC)都可以用于时区/当前时间。