mexico_city datetimezone date_default_timezone_set america php validation datetime time timezone

php - datetimezone - timezone laravel



¿Cómo verificar si el identificador de zona horaria es válido desde el código? (8)

  1. Ver en fuentes PHP en php_date.c y timezonemap.h por eso puedo decir que esto siempre está en 101.111111% de información estática (pero por compilación php).

  2. Si desea obtenerlo dinámicamente, use timezone_abbreviations_list como DateTimeZone :: listAbbreviations es un mapa para él.

  3. Como puede ver, todos estos valores son solo una lista llena de tiempo para la versión actual de PHP.

La solución mucho más rápida es simple: prepare de alguna manera un archivo estático con identificadores recuperados una vez por servidor durante la instalación de su aplicación y úselo.

Por ejemplo:

function isValidTZ($zone) { static $zones = null; if (null === $zones) { include $YOUR_APP_STORAGE . ''/tz_list.php''; } // isset is muuuch faster than array_key_exists and also than in_array // so you should work with structure like [key => 1] return isset($zones[$zone]); }

tz_list.php debería ser así:

<?php $zones = array( ''Africa/Abidjan'' => 1, ''Africa/Accra'' => 1, ''Africa/Addis_Ababa'' => 1, // ... );

Trataré de explicar cuál es el problema aquí.

De acuerdo con la lista de zonas horarias compatibles del manual de PHP, puedo ver todos los identificadores TZ válidos en PHP.

Mi primera pregunta es cómo obtener esa lista del 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álida, de lo contrario debería devolver FALSE .

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

Entonces, cuando paso el identificador TZ usando $timezoneId (cadena) en la función, necesito un resultado booleano.

Bueno, lo que tengo hasta ahora ...

1) Solución usando el operador @

La 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 }

Eso funciona perfectamente, pero quiero otra solución (para evitar tratar de establecer una zona horaria incorrecta)

2) Solución usando timezone_identifiers_list()

Luego, estaba tratando de obtener una lista de identificadores válidos de zona horaria y in_array() con el parámetro usando la función in_array() . Así que traté de usar timezone_identifiers_list() , pero eso no fue tan bueno porque faltaban muchas zonas horarias en la matriz devuelta por esta función (alias de DateTimeZone::listIdentifiers() ). A primera vista, eso 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 me di cuenta de que la matriz $zoneList contiene ~ 400 elementos. De acuerdo con mis cálculos, debería devolver más de 550 elementos. Más de 150 elementos faltan ... Así que eso 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. Utilizando la matriz devuelta por este método, puedo extraer todos los identificadores de zona horaria admitidos 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 el Example #2 tengo solo 407 ).

Intenté encontrar diferencias entre esas 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 todos los identificadores TZ válidos que el Example #2 no pudo coincidir.

Hay cuatro identificadores TZ más (parte de $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, tengo una solución casi 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 puedo usarla. Por supuesto, utilizo esta función como parte de la clase, así que no necesito generar $zoneList en cada llamada a métodos.

Lo que realmente necesito aquí?

Me pregunto, ¿hay alguna solución más fácil (más rápida) para obtener una lista de todos los identificadores de zona horaria válidos como matriz (quiero evitar extraer esa lista de DateTimeZone::listAbbreviations() si eso es posible)? O si conoce otra forma de comprobar que el parámetro de zona horaria es válido , hágamelo saber (repito, el operador @ no puede ser parte de la solución).

PD: si necesita más detalles y ejemplos, hágamelo saber. Supongo que no.

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

Actualizar

Cualquier parte del código que arroje una excepción en la cadena de zona horaria no válida (oculto usando @ o atrapado usando el bloque try..catch ) no es la solución que estoy buscando.

Otra actualización

¡He puesto una pequeña recompensa en esta pregunta!

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


¿Por qué no usar @ operator? Este código funciona bastante bien y no cambias la zona horaria predeterminada:

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

Si no quieres @, puedes hacer:

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


Cuando probé esto en un sistema Linux que ejecuta 5.3.6, su Ejemplo # 2 me dio 411 zonas y el Ejemplo # 3 dio 496. La siguiente pequeña modificación al Ejemplo # 2 me da 591:

$zoneList = timezone_identifiers_list(DateTimeZone::ALL_WITH_BC);

No hay zonas devueltas por el Ejemplo n. ° 3 que no se devuelven con el Ejemplo n. ° 2 modificado.

En un sistema OS X que ejecuta 5.3.3, el Ejemplo # 2 da 407, el Ejemplo # 3 da 564, y el Ejemplo # 2 modificado da 565. Nuevamente, no hay zonas devueltas por el Ejemplo # 3 que no se devuelven con ese Ejemplo modificado # 2.

En un sistema Linux que ejecuta 5.2.6 con la extensión timezonedb PECL instalada, el ejemplo n. ° 2 me da 571 zonas y el ejemplo n. ° 3 me da solo 488. No hay zonas devueltas por el ejemplo n. ° 3 que no sean del ejemplo n. ° 2 de este sistema . La constante DateTimeZone :: ALL_WITH_BC no parece existir en 5.2.6; probablemente fue agregado en 5.3.0.

Por lo tanto, parece que la forma más sencilla de obtener una lista de todas las zonas horarias en 5.3.x es timezone_identifiers_list(DateTimeZone::ALL_WITH_BC) , y en 5.2.x es timezone_identifiers_list() . La forma más sencilla (si no la más rápida) de verificar si una cadena en particular es una zona horaria válida es probablemente @timezone_open($timezoneId) !== false .


En caso de php <5.3, ¿qué tal esto?

public static function is_valid_timezone($timezone) { $now_timezone = @date_default_timezone_get(); $result = @date_default_timezone_set($timezone); if( $now_timezone ){ // set back to current timezone date_default_timezone_set($now_timezone); } return $result; }


Investigaría qué cambia la matriz perfecta y usaría un mecanismo de almacenamiento en caché básico (como almacenar la matriz en un archivo, que incluiría y actualizaría cuando fuera necesario). Actualmente está optimizando la construcción de una matriz que es estática para el 99.9999% de todas las solicitudes.

Editar: 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 código debe regenerarse automáticamente.


Si está en la mayoría de Linux, si no toda la información sobre husos horarios allí almacenada en /usr/share/zoneinfo/ . Puede recorrerlos usando is_file() y funciones relacionadas.

También puede analizar los archivos anteriores con zdump para obtener códigos o buscar fuentes para estos archivos y grep / recortar la información necesaria. Nuevamente, no está obligado a usar funciones integradas para realizar la tarea. No existe ninguna razón por la cual alguien lo obligue a usar solo las funciones de fecha incorporadas.


Solo una adición a la excelente respuesta de Cal. Creo que lo siguiente podría ser aún más rápido ...

function isValidTimezoneID($tzid) { if (empty($tzid)) { return false; } foreach (timezone_abbreviations_list() as $zone) { foreach ($zone as $item) { if ($item["timezone_id"] == $tzid) { return true; } } } return false; }


Tu solución funciona bien, así que si es la velocidad lo que estás buscando, miraría más de cerca lo que estás haciendo con tus matrices. He cronometrado unos miles de ensayos para obtener tiempos promedio razonables, y estos son los resultados:

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

Aquí está la función más rápida:

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

La prueba if es más rápida si la reduce a solo if ($item[''timezone_id'']) , pero en lugar de ejecutarla 489 veces para capturar una sola caja, es más rápido desarmar la clave vacía después.

Configurar las teclas hash nos permite omitir la llamada array_unique() que es más costosa. Ordenar las claves y luego extraerlas es un poco más rápido que extraerlas y luego ordenar la lista extraída.

Si deja caer la clasificación (que no es necesaria a menos que esté comparando la lista), se reduce a 12,339 microsegundos.

Pero realmente, no necesitas devolver las llaves de todos modos. En cuanto a holistic isValidTimezoneId() , sería mejor que hicieras 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 solo necesita probar una vez por ejecución, de lo contrario, desea guardar $valid después de la primera ejecución. Este enfoque evita tener que hacer una clasificación o convertir las claves a valores, las búsquedas de teclas son más rápidas que las búsquedas en_arrays () y no hay llamadas de funciones adicionales. Establecer los valores de matriz en true lugar de 1 también elimina un solo molde cuando el resultado es verdadero.

Esto lo lleva de manera confiable a menos de 12 ms en mi máquina de prueba, casi la mitad del tiempo de su ejemplo. ¡Un experimento divertido en micro optimizaciones!