如何检查时区标识符是否有效?

Wh1*_*Ck5 32 php validation time timezone datetime

我会试着解释一下这里的问题.

根据PHP手册中支持的时区列表,我可以在PHP中看到所有有效的TZ标识符.

我的第一个问题是如何从代码中获取该列表,但这不是我真正需要的.

我的最终目标是编写函数isValidTimezoneId(),如果时区有效则返回TRUE,否则返回FALSE.

function isValidTimezoneId($timezoneId) {
  # ...function body...
  return ?; # TRUE or FALSE
  }
Run Code Online (Sandbox Code Playgroud)

所以,当我$timezoneId在函数中使用(字符串)传递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
  }
Run Code Online (Sandbox Code Playgroud)

这很好用,但我想要另一个解决方案(避免设置错误的时区)

2)使用timezone_identifiers_list()的解决方案

然后,我试图获取有效时区标识符的列表,并使用in_array()函数对参数进行检查.所以我尝试使用timezone_identifiers_list(),但这并不是很好,因为这个函数返回的数组中缺少很多时区(DateTimeZone :: listIdentifiers()的别名).乍一看,这正是我所寻找的.

function isValidTimezoneId($timezoneId) {
  $zoneList = timezone_identifiers_list(); # list of (all) valid timezones
  return in_array($timezoneId, $zoneList); # set result
  }
Run Code Online (Sandbox Code Playgroud)

这段代码看起来很简单,但我发现该$zoneList数组包含~400个元素.根据我的计算,它应该返回550+元素.缺少150多个元素......所以这不足以解决我的问题.

3)基于DateTimeZone :: listAbbreviations()的解决方案

这是我试图寻找完美解决方案的最后一步.使用此方法返回的数组,我可以提取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);
  }
Run Code Online (Sandbox Code Playgroud)

这个函数返回563个元素(Example #2只有407个).

我试图找出这两个数组之间的差异:

$a1 = timezone_identifiers_list();
$a2 = createTZlist();

print_r(array_values(array_diff($a2, $a1)));
Run Code Online (Sandbox Code Playgroud)

结果是:

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
)
Run Code Online (Sandbox Code Playgroud)

此列表包含Example #2无法匹配的所有有效TZ标识符.

有四个TZ标识符(部分$a1):

print_r(array_values(array_diff($a1, $a2)));
Run Code Online (Sandbox Code Playgroud)

产量

Array
(
    [0] => America/Bahia_Banderas
    [1] => Antarctica/Macquarie
    [2] => Pacific/Chuuk
    [3] => Pacific/Pohnpei
)
Run Code Online (Sandbox Code Playgroud)

所以现在,我有几乎完美的解决方案......

function isValidTimezoneId($timezoneId) {
  $zoneList = createTZlist(); # list of all valid timezones (last 4 are not included)
  return in_array($timezoneId, $zoneList); # set result
  }
Run Code Online (Sandbox Code Playgroud)

这是我的解决方案,我可以使用它.当然,我使用这个函数作为类的一部分,所以我不需要$zoneList在每个方法调用上生成.

我真的需要什么?

我想知道,有没有更简单(更快)的解决方案来获取所有有效时区标识符的列表作为数组(我想避免从DateTimeZone::listAbbreviations()可能的那里提取该列表)?或者如果您知道另一种检查方法是时区参数有效,请告诉我(我再说一遍,@操作员不能成为解决方案的一部分).


PS如果您需要更多详细信息和示例,请与我们联系.我猜你没有.

我正在使用PHP 5.3.5(认为​​这不重要).


更新

在无效时区字符串上抛出异常的任何代码部分(使用@或使用try..catch块隐藏)都不是我正在寻找的解决方案.


另一个更新

我在这个问题上放了小小的赏金!

现在我正在寻找如何提取PHP数组中所有时区标识符列表的最简单方法.

hec*_*rct 24

为什么不使用@运算符?此代码运行良好,您不会更改默认时区:

function isValidTimezoneId($timezoneId) {
    @$tz=timezone_open($timezoneId);
    return $tz!==FALSE;
}
Run Code Online (Sandbox Code Playgroud)

如果你不想要@,你可以这样做:

function isValidTimezoneId($timezoneId) {
    try{
        new DateTimeZone($timezoneId);
    }catch(Exception $e){
        return FALSE;
    }
    return TRUE;
} 
Run Code Online (Sandbox Code Playgroud)

  • 尝试捕获解决方案是最好的imho.不错的OO解决方案. (6认同)
  • @ Wh1t3h4Ck5谢谢.我做了一个测试,你的代码比我的两倍快.哇!我不知道尝试创建Timezone对象是如此昂贵. (2认同)

Cal*_*Cal 16

你的解决方案工作正常,所以如果你正在寻找它的速度,我会更仔细地看你正在对阵列做什么.我花了几千次试验来获得合理的平均时间,结果如下:

createTZlist  : 20,713 microseconds per run
createTZlist2 : 13,848 microseconds per run
Run Code Online (Sandbox Code Playgroud)

这是更快的功能:

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);
}
Run Code Online (Sandbox Code Playgroud)

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];
}
Run Code Online (Sandbox Code Playgroud)

也就是说,假设您每次执行只需要测试一次,否则您希望$valid在第一次运行后保存.这种方法避免了必须进行排序或将键转换为值,键查找比in_array()搜索更快,并且没有额外的函数调用.设置数组值true而不是1在结果为true时删除单个强制转换.

这使我的测试机器上的可靠性低至12毫秒,几乎是示例时间的一半.微优化的有趣实验!


Ano*_*mie 5

当我在运行5.3.6的Linux系统上尝试此操作时,您的示例#2给了我411个区域,示例#3给了496.以下对示例#2的稍作修改给了我591:

$zoneList = timezone_identifiers_list(DateTimeZone::ALL_WITH_BC);
Run Code Online (Sandbox Code Playgroud)

示例#3返回的区域没有返回修改后的示例#2.

在运行5.3.3的OS X系统上,示例#2给出407,示例#3给出564,修改的示例#2给出565.同样,示例#3返回的区域没有返回该修改的示例#2.

在安装了timezonedb PECL扩展的运行5.2.6的Linux系统上,示例#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.