Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
201 views
in Technique[技术] by (71.8m points)

php - How to check is timezone identifier valid from code?

I'll try to explain what's the problem here.

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.

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
  }

So, when I pass TZ identifier using $timezoneId (string) in function I need boolean result.

Well, what I have so far...

1) Solution using @ operator

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)

2) Solution using timezone_identifiers_list()

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
  }

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.

3) Solution based on DateTimeZone::listAbbreviations()

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

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

Result is:

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
)

This list contains all valid TZ identifiers that Example #2 failed to match.

There's four TZ identifiers more (part of $a1):

print_r(array_values(array_diff($a1, $a2)));

Output

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
  }

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.

What I really need here?

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.

I'm using PHP 5.3.5 (think that's not important).


Update

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.


Another update

I've put small bounty on this question!

Now I'm looking for the easiest way how to extract list of all timezone identifiers in PHP array.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Why not use @ operator? This code works pretty well, and you don't change default timezone:

function isValidTimezoneId($timezoneId) {
    @$tz=timezone_open($timezoneId);
    return $tz!==FALSE;
}

If you don't want @, you can do:

function isValidTimezoneId($timezoneId) {
    try{
        new DateTimeZone($timezoneId);
    }catch(Exception $e){
        return FALSE;
    }
    return TRUE;
} 

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...