如何从代码中检查时区标识符是否有效?
我会试着解释这里的问题.
I'll try to explain what's the problem here.
根据 PHP 手册中的支持的时区列表,我可以在 PHP 中看到所有有效的 TZ 标识符.
According to list of supported timezones from PHP manual, I can see all valid TZ identifiers in PHP.
我的第一个问题是如何从代码中获取该列表,但这并不是我真正需要的.
My first question is how to get that list from code but that's not what I really need.
我的最终目标是编写函数 isValidTimezoneId()
如果时区有效则返回 TRUE,否则返回 FALSE.>
My final goal is to write function isValidTimezoneId()
that returns TRUE if timezone is valid, otherwise it should return FALSE.
function isValidTimezoneId($timezoneId) {
# ...function body...
return ?; # TRUE or FALSE
}
因此,当我在函数中使用 $timezoneId
(字符串)传递 TZ 标识符时,我需要布尔结果.
So, when I pass TZ identifier using $timezoneId
(string) in function I need boolean result.
嗯,到目前为止我所拥有的......
Well, what I have so far...
我得到的第一个解决方案是这样的:
First solution I've got is something like this:
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
}
效果很好,但我想要另一种解决方案(以避免尝试设置错误的时区)
That works perfectly, but I want another solution (to avoid trying to set wrong timezone)
然后,我试图获取有效时区标识符的列表,并使用 in_array() 对照参数检查它 函数.所以我尝试使用 timezone_identifiers_list(),但事实并非如此很好,因为此函数返回的数组中缺少很多时区(DateTimeZone::listIdentifiers()).乍一看,这正是我要找的.p>
Then, I was trying to get list of valid timezone identifiers and check it against parameter using in_array() function. So I've tried to use timezone_identifiers_list(), but that was not so good because a lot of timezones was missing in array returned by this function (alias of DateTimeZone::listIdentifiers()). At first sight that was exactly what I was looking for.
function isValidTimezoneId($timezoneId) {
$zoneList = timezone_identifiers_list(); # list of (all) valid timezones
return in_array($timezoneId, $zoneList); # set result
}
这段代码看起来不错而且简单,但我发现 $zoneList
数组包含约 400 个元素.根据我的计算,它应该返回 550+ 个元素.缺少 150 多个元素......所以这还不足以解决我的问题.
This code looks nice and easy but than I've found that $zoneList
array contains ~400 elements. According to my calculations it should return 550+ elements. 150+ elements are missing... So that's not good enough as solution for my problem.
这是我试图找到完美解决方案的最后一步.使用此方法返回的数组,我可以提取所有 PHP 支持的时区标识符.
This is last step on my way trying to find perfect solution. Using array returned by this method I can extract all timezone identifiers supported by PHP.
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 个).
This function returns 563 elements (in Example #2
I've got just 407).
我试图找出这两个数组之间的差异:
I've tried to find differences between those two arrays:
$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 标识符.
This list contains all valid TZ identifiers that Example #2
failed to match.
还有四个 TZ 标识符($a1
的一部分):
There's four TZ identifiers more (part of $a1
):
print_r(array_values(array_diff($a1, $a2)));
输出
Array
(
[0] => America/Bahia_Banderas
[1] => Antarctica/Macquarie
[2] => Pacific/Chuuk
[3] => Pacific/Pohnpei
)
现在,我有了近乎完美的解决方案......
So now, I have almost perfect solution...
function isValidTimezoneId($timezoneId) {
$zoneList = createTZlist(); # list of all valid timezones (last 4 are not included)
return in_array($timezoneId, $zoneList); # set result
}
这是我的解决方案,我可以使用它.当然,我使用这个函数作为类的一部分,所以我不需要在每次方法调用时生成 $zoneList
.
That's my solution and I can use it. Of course, I use this function as part of class so I don't need to generate $zoneList
on every methods call.
我想知道,是否有任何更简单(更快)的解决方案来获取所有有效时区标识符的列表作为数组(我想避免从 DateTimeZone::listAbbreviations()
中提取该列表,如果是这样的话可能的)?或者,如果您知道如何检查时区参数是否有效的另一种方法,请告诉我(我再说一遍,@
运算符不能成为解决方案的一部分).
I'm wondering, is there any easier (quicker) solution to get list of all valid timezone identifiers as array (I want to avoid extracting that list from DateTimeZone::listAbbreviations()
if that's possible)? Or if you know another way how to check is timezone parameter valid, please let me know (I repeat, @
operator can't be part of solution).
<小时>附言如果您需要更多详细信息和示例,请告诉我.我猜你没有.
P.S. If you need more details and examples, let me know. I guess you don't.
我使用的是 PHP 5.3.5
(认为这不重要).
I'm using PHP 5.3.5
(think that's not important).
任何在无效时区字符串上抛出异常的代码部分(使用 @
隐藏或使用 try..catch
块捕获)都不是我正在寻找的解决方案.
Any part of code that throws exception on invalid timezone string (hidden using @
or caught using try..catch
block) is not solution I'm looking for.
我已经为这个问题付出了小代价!
现在我正在寻找如何在 PHP 数组中提取所有时区标识符列表的最简单方法.
Now I'm looking for the easiest way how to extract list of all timezone identifiers in PHP array.
推荐答案
您的解决方案运行良好,因此如果您正在寻找速度,我会更仔细地查看您对数组所做的工作.我已经对几千次试验进行计时以获得合理的平均时间,结果如下:
You solution works fine, so if it's speed you're looking for I would look more closely at what you're doing with your arrays. I've timed a few thousand trials to get reasonable average times, and these are the results:
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 次来捕获单个情况下,之后取消设置空键会更快.
The if
test is faster if you reduce it to just if ($item['timezone_id'])
, but rather than running it 489 times to catch a single case, it's quicker to unset the empty key afterwards.
设置哈希键允许我们跳过更昂贵的 array_unique()
调用.对键进行排序然后提取它们比提取它们然后对提取的列表进行排序要快一点.
Setting hash keys allows us the skip the array_unique()
call which is more expensive. Sorting the keys and then extracting them is a tiny bit faster than extracting them and then sorting the extracted list.
如果您放弃排序(除非您比较列表,否则不需要排序),它会减少到 12,339 微秒.
If you drop the sorting (which is not needed unless you're comparing the list), it gets down to 12,339 microseconds.
但实际上,您无论如何都不需要归还密钥.查看整体 isValidTimezoneId()
,您最好这样做:
But really, you don't need to return the keys anyway. Looking at the holistic isValidTimezoneId()
, you'd be better off doing this:
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
也会在结果为真时删除单个强制转换.
That is, assuming you only need to test once per execution, otherwise you'd want to save $valid
after the first run. This approach avoids having to do a sort or converting the keys to values, key lookups are faster than in_array() searches and there's no extra function call. Setting the array values to true
instead of 1
also removes a single cast when the result is true.
这使它在我的测试机器上可靠地降低到 12 毫秒以下,几乎是您示例时间的一半.一个有趣的微优化实验!
This brings it reliably down to under 12ms on my test machine, almost half the time of your example. A fun experiment in micro-optimizations!
相关文章