sql >> Base de Datos >  >> RDS >> Mysql

PHP, MySQL y zonas horarias

Esta respuesta se ha actualizado para acomodar la recompensa. La respuesta original, sin editar, está debajo de la línea.

Casi todos los puntos de pregunta agregados por el propietario de la recompensa están relacionados con cómo deben interactuar las fechas y horas de MySQL y PHP, en el contexto de las zonas horarias.

MySQL todavía tiene patético soporte de zona horaria , lo que significa que la inteligencia tiene que ser del lado de PHP.

  • Configure su zona horaria de conexión de MySQL a UTC como se documenta en el enlace anterior. Esto hará que todas las fechas y horas sean manejadas por MySQL, incluido NOW() , para ser manejado con sensatez.
  • Utilice siempre DATETIME , nunca use TIMESTAMP a menos que requiera muy expresamente el comportamiento especial en un TIMESTAMP . Esto es menos doloroso de lo que solía ser.
    • Está bien para almacenar el tiempo de época de Unix como un número entero si tiene a, como para fines heredados. La época es UTC.
    • El formato de fecha y hora preferido de MySQL se crea utilizando la cadena de formato de fecha de PHP Y-m-d H:i:s
  • Convertir todo PHP datetimes a UTC al almacenarlos en MySQL, lo cual es algo trivial como se describe a continuación
  • Las fechas y horas devueltas por MySQL se pueden entregar de manera segura al constructor PHP DateTime. ¡Asegúrate de pasar también en una zona horaria UTC!
  • Convierta PHP DateTime a la zona horaria local del usuario on echo , no antes. Afortunadamente, la comparación de DateTime y las matemáticas con otros DateTimes tendrán en cuenta la zona horaria en la que se encuentra cada uno.
  • Todavía está a la altura de los caprichos de la base de datos DST provista con PHP. ¡Mantenga sus parches PHP y OS actualizados! Mantenga MySQL en el maravilloso estado UTC para eliminar una posible molestia de DST.

Eso aborda la mayoría de los puntos.

Lo último es un doozy:

  • ¿Qué debe hacer alguien si ha insertado datos previamente (por ejemplo, usando NOW()) ) sin preocuparte por la zona horaria para asegurarte de que todo se mantenga constante?

Esto es una verdadera molestia. Una de las otras respuestas señaló MySQL's CONVERT_TZ , aunque personalmente lo habría hecho saltando entre las zonas horarias nativas del servidor y UTC durante las selecciones y actualizaciones, porque soy así de duro.

la aplicación también debería poder CONFIGURAR/Elegir el horario de verano en consecuencia para cada usuario.

No es necesario y no debería hacer esto en la era moderna.

Las versiones modernas de PHP tienen el DateTimeZone class, que incluye la capacidad de listar zonas horarias con nombre . Las zonas horarias con nombre permiten al usuario seleccionar su ubicación real y hacer que el sistema automáticamente determinar sus reglas de horario de verano en función de esa ubicación.

Puede combinar DateTimeZone con DateTime para una funcionalidad simple pero poderosa. Simplemente puede almacenar y usar todos de sus marcas de tiempo en UTC por defecto y convertirlos a la zona horaria del usuario en pantalla.

// UTC default
    date_default_timezone_set('UTC');
// Note the lack of time zone specified with this timestamp.
    $nowish = new DateTime('2011-04-23 21:44:00');
    echo $nowish->format('Y-m-d H:i:s'); // 2011-04-23 21:44:00
// Let's pretend we're on the US west coast.  
// This will be PDT right now, UTC-7
    $la = new DateTimeZone('America/Los_Angeles');
// Update the DateTime's timezone...
    $nowish->setTimeZone($la);
// and show the result
    echo $nowish->format('Y-m-d H:i:s'); // 2011-04-23 14:44:00

Al utilizar esta técnica, el sistema automáticamente seleccione la configuración de horario de verano correcta para el usuario, sin preguntarle si está actualmente en horario de verano o no.

Puede usar un método similar para representar el menú de selección. Puede reasignar continuamente la zona horaria para el único objeto DateTime. Por ejemplo, este código enumerará las zonas y sus horarios actuales, en este momento:

$dt = new DateTime('now', new DateTimeZone('UTC')); 
foreach(DateTimeZone::listIdentifiers() as $tz) {
    $dt->setTimeZone(new DateTimeZone($tz));
    echo $tz, ': ', $dt->format('Y-m-d H:i:s'), "\n";
}

Puede simplificar enormemente el proceso de selección utilizando algo de magia del lado del cliente. Javascript tiene un irregular pero funcional Clase de fecha, con un método estándar para obtener el desplazamiento UTC en minutos . Puede usar esto para ayudar a reducir la lista de zonas horarias probables, bajo la suposición ciega de que el reloj del usuario está bien.

Comparemos este método con hacerlo usted mismo. Tendría que realizar matemáticas de fecha todas las veces manipulas una fecha y hora, además de empujar una elección en el usuario que realmente no le va a importar. Esto no es solo subóptimo, es una locura de guano de murciélago. Obligar a los usuarios a indicar cuándo quieren compatibilidad con el horario de verano es generar problemas y confusión.

Además, si desea utilizar el marco moderno PHP DateTime y DateTimeZone para esto, deberá usar obsoleto Etc/GMT... cadenas de zonas horarias en lugar de zonas horarias con nombre. Es posible que estos nombres de zona se eliminen de futuras versiones de PHP, por lo que no sería prudente hacerlo. Todo esto lo digo por experiencia.

tl;dr :Use el conjunto de herramientas moderno, ahórrese los horrores de las matemáticas de fechas. Presentar al usuario una lista de nombrados zonas horarias. Guarda tus fechas en UTC , que no se verá afectado por el horario de verano de ninguna manera. Convierta fechas y horas a la zona horaria nombrada seleccionada por el usuario en pantalla , no antes.

Según lo solicitado, aquí hay un bucle sobre las zonas horarias disponibles que muestra su diferencia GMT en minutos. Seleccioné minutos aquí para demostrar un hecho desafortunado:¡no todas las compensaciones están en horas enteras! Algunos en realidad cambian media hora antes durante el horario de verano en lugar de una hora entera. El desplazamiento resultante en minutos debería coincida con el de Date.getTimezoneOffset de Javascript .

$utc = new DateTimeZone('UTC');
$dt = new DateTime('now', $utc); 
foreach(DateTimeZone::listIdentifiers() as $tz) {
    $local = new DateTimeZone($tz);
    $dt->setTimeZone($local);
    $offset = $local->getOffset($dt); // Yeah, really.
    echo $tz, ': ', 
         $dt->format('Y-m-d H:i:s'),
         ', offset = ',
         ($offset / 60),
         " minutes\n";
}