我试着解释一下这里有什么问题。
根据PHP手册中的支持的时区列表,我可以在PHP中看到所有有效的TZ标识符。
我的第一个问题是如何从代码中获取列表,但这不是我真正需要的。
我的最后一个目标是编写函数isValidTimezoneId(),如果时区有效,它将返回TRUE,否则它应该返回FALSE。
function isValidTimezoneId($timezoneId) {
# ...function body...
return ?; # TRUE or FALSE
}因此,当我在函数中使用$timezoneId (string)传递TZ标识符时,我需要布尔结果。
好吧,到目前为止.
1)使用@运算符的解
我得到的第一个解决方案是这样的:
function isValidTimezoneId($timezoneId) {
$savedZone = date_default_timezone_get(); # save current zone
$res = $savedZone == $timezoneId; # it's TRUE if param matches current zone
if (!$res) { # 0r...
@date_default_timezone_set($timezoneId); # try to set new timezone
$res = date_default_timezone_get() == $timezoneId; # it's true if new timezone set matches param string.
}
date_default_timezone_set($savedZone); # restore back old timezone
return $res; # set result
}这是完美的,但我需要另一个解决方案(以避免试图设置错误的时区)。
2)使用列表()的解决方案
然后,我试图获取有效时区标识符的列表,并使用数组()函数对其进行参数检查。所以我尝试使用列表(),但这并不好,因为这个函数(DateTimeZone::list标识符()的别名)返回的数组中缺少了很多时区。乍一看,这正是我要找的。
function isValidTimezoneId($timezoneId) {
$zoneList = timezone_identifiers_list(); # list of (all) valid timezones
return in_array($timezoneId, $zoneList); # set result
}这段代码看起来很简单,但比我发现的$zoneList数组包含了大约400个元素。根据我的计算,它应该返回550+元素。150+元素不见了..。所以这还不足以解决我的问题。
3)基于DateTimeZone::listAbbreviations()的解决方案
这是我努力寻找完美解决方案的最后一步。使用此方法返回的数组,我可以提取支持的所有时区标识符。
function createTZlist() {
$tza = DateTimeZone::listAbbreviations();
$tzlist = array();
foreach ($tza as $zone)
foreach ($zone as $item)
if (is_string($item['timezone_id']) && $item['timezone_id'] != '')
$tzlist[] = $item['timezone_id'];
$tzlist = array_unique($tzlist);
asort($tzlist);
return array_values($tzlist);
}这个函数返回563个元素(在Example #2中,只有407)。
我试图找出这两个数组之间的区别:
$a1 = timezone_identifiers_list();
$a2 = createTZlist();
print_r(array_values(array_diff($a2, $a1)));结果是:
Array
(
[0] => Africa/Asmera
[1] => Africa/Timbuktu
[2] => America/Argentina/ComodRivadavia
[3] => America/Atka
[4] => America/Buenos_Aires
[5] => America/Catamarca
[6] => America/Coral_Harbour
[7] => America/Cordoba
[8] => America/Ensenada
[9] => America/Fort_Wayne
[10] => America/Indianapolis
[11] => America/Jujuy
[12] => America/Knox_IN
[13] => America/Louisville
[14] => America/Mendoza
[15] => America/Porto_Acre
[16] => America/Rosario
[17] => America/Virgin
[18] => Asia/Ashkhabad
[19] => Asia/Calcutta
[20] => Asia/Chungking
[21] => Asia/Dacca
[22] => Asia/Istanbul
[23] => Asia/Katmandu
[24] => Asia/Macao
[25] => Asia/Saigon
[26] => Asia/Tel_Aviv
[27] => Asia/Thimbu
[28] => Asia/Ujung_Pandang
[29] => Asia/Ulan_Bator
[30] => Atlantic/Faeroe
[31] => Atlantic/Jan_Mayen
[32] => Australia/ACT
[33] => Australia/Canberra
[34] => Australia/LHI
[35] => Australia/NSW
[36] => Australia/North
[37] => Australia/Queensland
[38] => Australia/South
[39] => Australia/Tasmania
[40] => Australia/Victoria
[41] => Australia/West
[42] => Australia/Yancowinna
[43] => Brazil/Acre
[44] => Brazil/DeNoronha
[45] => Brazil/East
[46] => Brazil/West
[47] => CET
[48] => CST6CDT
[49] => Canada/Atlantic
[50] => Canada/Central
[51] => Canada/East-Saskatchewan
[52] => Canada/Eastern
[53] => Canada/Mountain
[54] => Canada/Newfoundland
[55] => Canada/Pacific
[56] => Canada/Saskatchewan
[57] => Canada/Yukon
[58] => Chile/Continental
[59] => Chile/EasterIsland
[60] => Cuba
[61] => EET
[62] => EST
[63] => EST5EDT
[64] => Egypt
[65] => Eire
[66] => Etc/GMT
[67] => Etc/GMT+0
[68] => Etc/GMT+1
[69] => Etc/GMT+10
[70] => Etc/GMT+11
[71] => Etc/GMT+12
[72] => Etc/GMT+2
[73] => Etc/GMT+3
[74] => Etc/GMT+4
[75] => Etc/GMT+5
[76] => Etc/GMT+6
[77] => Etc/GMT+7
[78] => Etc/GMT+8
[79] => Etc/GMT+9
[80] => Etc/GMT-0
[81] => Etc/GMT-1
[82] => Etc/GMT-10
[83] => Etc/GMT-11
[84] => Etc/GMT-12
[85] => Etc/GMT-13
[86] => Etc/GMT-14
[87] => Etc/GMT-2
[88] => Etc/GMT-3
[89] => Etc/GMT-4
[90] => Etc/GMT-5
[91] => Etc/GMT-6
[92] => Etc/GMT-7
[93] => Etc/GMT-8
[94] => Etc/GMT-9
[95] => Etc/GMT0
[96] => Etc/Greenwich
[97] => Etc/UCT
[98] => Etc/UTC
[99] => Etc/Universal
[100] => Etc/Zulu
[101] => Europe/Belfast
[102] => Europe/Nicosia
[103] => Europe/Tiraspol
[104] => Factory
[105] => GB
[106] => GB-Eire
[107] => GMT
[108] => GMT+0
[109] => GMT-0
[110] => GMT0
[111] => Greenwich
[112] => HST
[113] => Hongkong
[114] => Iceland
[115] => Iran
[116] => Israel
[117] => Jamaica
[118] => Japan
[119] => Kwajalein
[120] => Libya
[121] => MET
[122] => MST
[123] => MST7MDT
[124] => Mexico/BajaNorte
[125] => Mexico/BajaSur
[126] => Mexico/General
[127] => NZ
[128] => NZ-CHAT
[129] => Navajo
[130] => PRC
[131] => PST8PDT
[132] => Pacific/Ponape
[133] => Pacific/Samoa
[134] => Pacific/Truk
[135] => Pacific/Yap
[136] => Poland
[137] => Portugal
[138] => ROC
[139] => ROK
[140] => Singapore
[141] => Turkey
[142] => UCT
[143] => US/Alaska
[144] => US/Aleutian
[145] => US/Arizona
[146] => US/Central
[147] => US/East-Indiana
[148] => US/Eastern
[149] => US/Hawaii
[150] => US/Indiana-Starke
[151] => US/Michigan
[152] => US/Mountain
[153] => US/Pacific
[154] => US/Pacific-New
[155] => US/Samoa
[156] => Universal
[157] => W-SU
[158] => WET
[159] => Zulu
)此列表包含Example #2无法匹配的所有有效TZ标识符。
有四个TZ标识符更多($a1的一部分):
print_r(array_values(array_diff($a1, $a2)));输出
Array
(
[0] => America/Bahia_Banderas
[1] => Antarctica/Macquarie
[2] => Pacific/Chuuk
[3] => Pacific/Pohnpei
)所以现在我有了几乎完美的解决方案..。
function isValidTimezoneId($timezoneId) {
$zoneList = createTZlist(); # list of all valid timezones (last 4 are not included)
return in_array($timezoneId, $zoneList); # set result
}这是我的解决方案,我可以用它。当然,我使用这个函数作为类的一部分,所以我不需要在每个方法调用中生成$zoneList。
我在这里真正需要什么?
我想知道,是否有更简单(更快)的解决方案可以将所有有效的时区标识符列表作为数组(如果可能的话,我希望避免从DateTimeZone::listAbbreviations()中提取该列表)?或者,如果您知道另一种检查的方法是时区参数有效的,请告诉我(重复一遍,@运算符不能成为解决方案的一部分)。
如果你需要更多的细节和例子,请告诉我。我想你不知道。
我正在使用PHP 5.3.5 (认为这并不重要)。
更新
在无效时区字符串(使用@隐藏或使用try..catch块捕获)上抛出异常的代码的任何部分都不是我要寻找的解决方案。
另一次更新
,我已经在这个问题上投了点小赏金!
现在,我正在寻找如何提取数组中所有时区标识符列表的最简单方法。
发布于 2011-05-04 04:51:11
您的解决方案工作得很好,所以如果您要寻找的是速度,我会更仔细地查看您对数组所做的事情。为了获得合理的平均次数,我安排了几千次试验,结果如下:
createTZlist : 20,713 microseconds per run
createTZlist2 : 13,848 microseconds per run下面是一个更快的功能:
function createTZList2()
{
$out = array();
$tza = timezone_abbreviations_list();
foreach ($tza as $zone)
{
foreach ($zone as $item)
{
$out[$item['timezone_id']] = 1;
}
}
unset($out['']);
ksort($out);
return array_keys($out);
}如果将if测试简化为只使用if ($item['timezone_id']),那么它的测试速度会更快,但与其运行489次以捕获单个情况,不如在之后更快地取消空键设置。
设置哈希键允许我们跳过array_unique()调用,这是比较昂贵的。对密钥进行排序,然后提取它们,比提取密钥并对提取的列表进行排序要快一点点。
如果删除排序(除非比较列表,否则不需要排序),排序将降至12,339微秒。
但实际上你不需要把钥匙还给我。从整体isValidTimezoneId()来看,最好这样做:
function isValidTimezoneId2($tzid)
{
$valid = array();
$tza = timezone_abbreviations_list();
foreach ($tza as $zone)
{
foreach ($zone as $item)
{
$valid[$item['timezone_id']] = true;
}
}
unset($valid['']);
return !!$valid[$tzid];
}也就是说,假设每次执行只需要测试一次,否则您将希望在第一次运行后保存$valid。这种方法避免了进行排序或将键转换为值,键查找比in_array()搜索更快,而且没有额外的函数调用。当结果为真时,将数组值设置为true而不是1也会删除单个强制转换。
这样,在我的测试机器上,它可以可靠地降到不到12 my,几乎是示例的一半。一个有趣的实验,在微观优化!
发布于 2011-04-28 18:19:22
为什么不使用@操作符呢?这段代码运行得很好,并且不更改默认时区:
function isValidTimezoneId($timezoneId) {
@$tz=timezone_open($timezoneId);
return $tz!==FALSE;
}如果你不想要“,你可以:
function isValidTimezoneId($timezoneId) {
try{
new DateTimeZone($timezoneId);
}catch(Exception $e){
return FALSE;
}
return TRUE;
} 发布于 2011-05-03 17:05:36
当我在运行5.3.6的Linux系统上尝试时,您的示例2给出了411个区域,示例3给出了496个区域。下面对示例2的轻微修改给了我591:
$zoneList = timezone_identifiers_list(DateTimeZone::ALL_WITH_BC);示例3返回的区域中没有未与修改的示例2一起返回的区域。
在运行5.3.3的OS系统上,示例2给出407,示例3给出564,修改后的示例2给出565。同样,示例3返回的区域中没有未与修改后的示例2一起返回的区域。
在安装了timezonedb PECL扩展的Linux5.2.6系统上,示例2给出了571个区域,示例3只给出了488个区域。在此系统中,没有示例3返回的区域不是示例2返回的区域。常数DateTimeZone::ALL_WITH_BC在5.2.6中似乎不存在;它可能是在5.3.0中添加的。
因此,获得5.3.x中所有时区列表的最简单方法是timezone_identifiers_list(DateTimeZone::ALL_WITH_BC),在5.2.x中是timezone_identifiers_list()。检查特定字符串是否是有效时区的最简单(如果不是最快的)方法仍然可能是@timezone_open($timezoneId) !== false。
https://stackoverflow.com/questions/5816960
复制相似问题