33 votos

¿Cómo comprobar es identificador de zona horaria válido desde código?

Voy a intentar explicar cuál es el problema aquí.

De acuerdo a la lista de las zonas horarias del manual de PHP, puedo ver todo válido TZ identificadores en PHP.

Mi primera pregunta es cómo conseguir que la lista de código, pero eso no es lo que realmente necesito.

Mi objetivo final es escribir la función isValidTimezoneId() que devuelve TRUE si la zona horaria es válido, de lo contrario debe devolver FALSE.

function isValidTimezoneId($timezoneId) {
  # ...function body...
  return ?; # TRUE or FALSE
  }

Así que, cuando me pase TZ identificador usando $timezoneId (string) en función necesito resultado booleano.

Bueno, lo que tengo hasta ahora...

1) Solución mediante el operador @

Primera solución que tengo es algo como esto:

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
  }

Que funciona perfectamente, pero quiero otra solución (para evitar tratando de establecer la zona horaria incorrecta)

2) utilizando la Solución timezone_identifiers_list()

Entonces, yo estaba tratando de obtener la lista de zona horaria válida identificadores y compararlo con parámetro mediante in_array() función. Así que he intentado utilizar timezone_identifiers_list(), pero eso no era tan bueno, porque un montón de zonas horarias que faltaba en el array devuelto por esta función (alias de DateTimeZone::listIdentifiers()). A primera vista, que era exactamente lo que estaba buscando.

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

Este código se ve bien y fácil, pero de lo que me he encontrado con que $zoneList matriz contiene ~400 elementos. Según mis cálculos debería volver 550+ elementos. 150+ elementos que faltan... Así que no es lo suficientemente bueno como solución para mi problema.

3) Solución basada en DateTimeZone::listAbbreviations()

Este es el último paso en mi camino tratando de encontrar la solución perfecta. El uso de array devuelto por este método que se puede extraer toda la zona horaria de identificadores soportadas por 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);
  }

Esta función devuelve 563 elementos (en Example #2 tengo solo 407).

He tratado de encontrar diferencias entre los dos matrices:

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

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

El resultado es:

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
)

Esta lista contiene todas válidas TZ identificadores Example #2 no se corresponden.

Hay cuatro TZ identificadores más (parte de la $a1):

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

Salida

Array
(
    [0] => America/Bahia_Banderas
    [1] => Antarctica/Macquarie
    [2] => Pacific/Chuuk
    [3] => Pacific/Pohnpei
)

Así que ahora, casi tengo la solución perfecta...

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

Esa es mi solución y la voy a usar. Por supuesto, yo uso esta función como parte de la clase, de modo que no necesito para generar $zoneList sobre todos los métodos de la llamada.

Lo que realmente necesito aquí?

Me pregunto, ¿hay alguna manera más fácil (más rápido) solución para obtener la lista de todas válidas zona horaria de identificadores como matriz (quiero evitar la extracción de la lista de DateTimeZone::listAbbreviations() , si eso es posible)? O si conocen otra forma de cómo comprobar es la zona horaria de parámetro válido, por favor hágamelo saber (repito, @ operador no puede ser parte de la solución).


P. S. Si usted necesita más detalles y ejemplos, que me haga saber. Supongo que no.

Estoy usando PHP 5.3.5 (creo que no es importante).


Actualización

Cualquier parte de código que lanza excepciones no válido en la zona horaria de cadena (oculto usando @ o capturados con try..catch bloque) no es la solución que estoy buscando.


Otra actualización

Me he puesto a la pequeña recompensa en esta pregunta!

Ahora estoy buscando la manera más fácil de cómo extraer la lista de todos los de la zona horaria identificadores en el array de PHP.

25voto

hectorct Puntos 1800

¿Por qué no usar a operador? Este código funciona bastante bien, y no cambiar zona horaria por defecto:

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

Si usted no quiere a, usted puede hacer:

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

17voto

Cal Puntos 5474

La solución funciona bien, por lo que si la velocidad a la que usted está buscando me gustaría ver más de cerca lo que está haciendo con sus matrices. He cronometrado de unos pocos miles de ensayos para obtener razonable promedio de los tiempos, y estos son los resultados:

createTZlist  : 20,713 microseconds per run
createTZlist2 : 13,848 microseconds per run

Aquí está el más rápido de la función:

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

El if prueba es más rápida si se reduzca solo a if ($item['timezone_id']), pero en lugar de ejecutarlo 489 veces para coger un solo caso, que es más rápido para desactivar la clave vacía después.

Configuración de claves hash nos permite saltar la array_unique() convocatoria que es más caro. Clasificación de las teclas y, a continuación, extraer de ellos es un poco más rápido que la extracción de ellos y, a continuación, ordenar que se extrae de la lista.

Si se le cae la clasificación (que no es necesario a menos que usted está comparando la lista), se llega a 12,339 microsegundos.

Pero, en realidad, usted no necesita devolver las llaves de todos modos. Buscando en la holístico isValidTimezoneId(), que estaría mejor haciendo esto:

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

Es decir, suponiendo que sólo se necesita la prueba de una vez por ejecución, de lo contrario te gustaría ahorrar $valid después de la primera carrera. Este enfoque evita tener que hacer un tipo de conversión o las llaves a los valores, clave de las búsquedas son más rápidas que in_array() busca y no hay ninguna llamada a la función. Configuración de los valores de la matriz a true en lugar de 1 también elimina de una sola pieza cuando el resultado es true.

Esto trae fiable por debajo de 12ms en mi máquina de prueba, casi la mitad del tiempo de su ejemplo. Un divertido experimento en el micro-optimizaciones!

6voto

Anomie Puntos 43759

Cuando he intentado esto en un sistema Linux se ejecuta 5.3.6, su Ejemplo #2 me dio 411 zonas y Ejemplo #3 dio 496. El siguiente ligera modificación Ejemplo #2 me da 591:

$zoneList = timezone_identifiers_list(DateTimeZone::ALL_WITH_BC);

No hay ninguna zona devuelto por Ejemplo #3 que no se devuelven con que se ha modificado el Ejemplo #2.

En un OS X sistema de ejecución de la sección 5.3.3, Ejemplo #2 da 407, Ejemplo #3 da 564, y la modificación de Ejemplo #2 da 565. De nuevo, no hay ninguna zona devuelto por Ejemplo #3 que no se devuelven con que se ha modificado el Ejemplo #2.

En un sistema Linux se ejecuta 5.2.6 con el timezonedb extensión PECL instalado, Ejemplo #2 me da 571 zonas y Ejemplo #3 me da sólo 488. No hay ninguna zona devuelto por Ejemplo #3 que no son por Ejemplo #2 en este sistema. La constante DateTimeZone::ALL_WITH_BC parece que no existe en 5.2.6; probablemente fue añadido en 5.3.0.

Por lo que parece la forma más sencilla para obtener una lista de todas las zonas horarias en 5.3.x es timezone_identifiers_list(DateTimeZone::ALL_WITH_BC), y en el 5.2.x es timezone_identifiers_list(). El más sencillo (si no la más rápida) para comprobar si una cadena es un tiempo de validez de la zona sigue siendo, seguramente, @timezone_open($timezoneId) !== false.

1voto

Mel Puntos 3000

Yo lo que cambia la matriz ideal de la investigación y utiliza un mecanismo básico de almacenamiento en caché (como almacenar la matriz en un archivo, que incluir y actualizar cuando sea necesario). Actualmente está optimizando una matriz estática de 99.9999% de todas las solicitudes de construcción.

Edit: Ok, estático/dinámico.

if( !function_exists(timezone_version_get) )
{
    function timezone_version_get() { return '2009.6'; }
}
include 'tz_list_' . PHP_VERSION . '_' . timezone_version_get() . '.php';

Ahora cada vez que se actualiza la versión de php, el archivo debe regenerarse automáticamente por el código.

1voto

sanmai Puntos 3990

Si estás en Linux la mayoría, si no toda la información sobre las zonas horarias que hay almacenado en /usr/share/zoneinfo/. Se puede caminar sobre ellos usando is_file() y funciones relacionadas.

También puede analizar la ex archivos con zdump para los códigos o ir a buscar las fuentes de estos archivos y grep/corte información necesaria. De nuevo, usted no está obligado a utilizar las funciones integradas para realizar la tarea. No hay una razón por qué iba alguien te obligan a usar sólo la incorporada en las funciones de fecha.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X