servidor - cambiar zona horaria phpmyadmin
PHP, MySQL y zonas horarias (3)
Cómo almacenar los tiempos en la base de datos desde un objeto PHP DateTime ¿Deberían almacenarse en DATETIME o TIMESTAMP? ¿Cuáles son los beneficios o advertencias para cada uno?
* ACTUALIZAR, aclarar mi primer párrafo * También puede almacenar una marca de tiempo como INT. La ventaja es que usted sabe en qué zona horaria ha almacenado su valor, ya que la marca de tiempo es la hora actual medida en el número de segundos desde la Época Unix (1 de enero de 1970 a las 00:00:00 GMT). vea php doc: http://php.net/manual/en/function.time.php Usando un sistema operativo de 64 bits, no debería tener que preocuparse por el problema del año 2038: http://en.wikipedia.org/wiki/Year_2038_problem
Timestamp es mucho más fácil de usar para comparar fechas y más diversión para usar en objet y array. Podría usarlos fácilmente como claves para sus matrices, por ejemplo.
¿Alguna vez tenemos que preocuparnos por las zonas horarias con FECHA DE MySQL?
En MySQL, las funciones CURRENT_TIMESTAMP (), CURRENT_TIME (), CURRENT_DATE () y FROM_UNIXTIME () devuelven valores en la zona horaria actual de la conexión, que está disponible como el valor de la variable del sistema time_zone. Además, UNIX_TIMESTAMP () asume que su argumento es un valor de fecha y hora en la zona horaria actual. http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html
Cómo insertar valores usando NOW (). ¿Es necesario convertir estos de alguna manera antes o después del inserto?
Si usa la marca de tiempo, puede confiar en la función PHP, es solo un número entero.
Si usa la fecha y hora, la función de fecha actual le permite tener la fecha actual. http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_curdate
¿Es necesario establecer el huso horario utilizado por MySQL? ¿Si es así, cómo? ¿Debería hacerse de forma persistente o en cada solicitud HTTP? ¿Tiene que establecerse en UTC o puede ser algo más? ¿O es suficiente el tiempo del servidor?
ver http://dev.mysql.com/doc/refman/5.0/en/time-zone-support.html
Cómo recuperar valores de MySQL y convertirlos a un objeto DateTime. ¿Bastará con ponerlo en DateTime :: __ construct () o necesitamos usar DateTime :: createFromFormat ()?
De nuevo, si usa la marca de tiempo, es más fácil. Ya conoce la zona horaria de la marca de tiempo, sabe cuándo cuándo convertirla a la hora local y por qué. El horario de verano es fácil de administrar con la marca de tiempo, vea las funciones alrededor de la marca de tiempo: http://php.net/manual/en/function.mktime.php
¿Hay algún momento en el que quisiéramos convertirlo antes de devolverlo al usuario (por ejemplo, compararlo con otro objeto DateTime o un valor estático)?
Creo que de nuevo, la marca de tiempo te permite trabajar con tu fecha para compararlos, extraer lo que necesites e imprimir lo que quieras.
¿Alguna vez tenemos que preocuparnos por el horario de verano (DST)? ¿Por qué o por qué no? ¿Qué debería hacer alguien si previamente ha insertado datos (por ejemplo, usando NOW ()) sin preocuparse por el huso horario para asegurarse de que todo se mantenga constante?
Sí, debe preocuparse si tiene que crear una cita o reunión en una aplicación. Desarrollé dos aplicaciones, una para citas clínicas y otra para talleres que respaldan más de 70000 cuentas y enormes cantidades de registros. Me quedo con la marca de tiempo, es súper preparado para indexar, manipular, comparar. La parte de impresión solo aparece en la vista.
Hay ventajas de usar datetime en su base de datos. Si tiene que analizar los datos de la tabla en SQL directo, es mucho más fácil de leer, es más "legible por humanos".
No estoy seguro de que haya una respuesta fija para esta publicación, ya que depende de sus necesidades. Timestamp es muy fácil de manipular para las operaciones (un enfoque pragmático). La forma de almacenarlo depende de su preferencia, ya que puede almacenar una fecha y aún convertirla a la marca de tiempo más tarde. Pero la zona horaria es parte de la definición de marcas de tiempo por lo que entiendo.
Intento integrar un sistema de zona horaria en mi aplicación, realmente he tratado de evitar hacer aplicaciones compatibles con la zona horaria, pero ahora es un requisito obligatorio, así que no tengo otra opción. TimeZones simplemente pasa por encima de mi cabeza. He leído varios temas en PHP.net y también en otros sitios, incluido, entre otros, SO. Pero nunca pude entenderlo.
Así que me preguntaba si alguien me puede ayudar aquí :( Lo que estoy buscando hacer es una opción de preferencia en mi aplicación para permitir a los usuarios elegir sus propias zonas horarias desde un menú de selección, pero la aplicación también debería ser capaz de SET / Elija el horario de verano en sí mismo para cada usuario.
Por favor, estoy seguro de que esto ayudará a otros que todavía están tratando de dominar las zonas horarias, así que proporcione la explicación más detallada posible, incluso si tiene que considerarme un dumbo / noob completo.
Editar por generosidad:
Estoy agregando una recompensa a esta pregunta porque realmente creo que necesitamos una buena pregunta canónica sobre las zonas horarias al escribir aplicaciones PHP / MySQL (así también estoy agregando la etiqueta MySQL). He encontrado cosas de muchos lugares, pero sería bueno tenerlo todo junto. La respuesta de Charles es genial, pero todavía siento que le falta un poco. Aquí hay algunas cosas en las que pensé:
- Cómo almacenar los tiempos en la base de datos desde un objeto PHP
DateTime
- ¿Deberían almacenarse en
DATETIME
oTIMESTAMP
? ¿Cuáles son los beneficios o advertencias para cada uno? - ¿Alguna vez tenemos que preocuparnos por las zonas horarias con
DATE
MySQL? - Cómo insertar valores usando
NOW()
. ¿Es necesario convertir estos de alguna manera antes o después del inserto? - ¿Es necesario establecer el huso horario utilizado por MySQL? ¿Si es así, cómo? ¿Debería hacerse de forma persistente o en cada solicitud HTTP? ¿Tiene que establecerse en UTC o puede ser algo más? ¿O es suficiente el tiempo del servidor?
- Cómo recuperar valores de MySQL y convertirlos a un objeto
DateTime
. ¿Bastará con ponerlo enDateTime::__construct()
o necesitamos usarDateTime::createFromFormat()
? - Cuándo convertir a la hora local y por qué. ¿Hay algún momento en el que quisiéramos convertirlo antes de devolverlo al usuario (por ejemplo, compararlo con otro objeto DateTime o un valor estático)?
- ¿Alguna vez tenemos que preocuparnos por el horario de verano (DST)? ¿Por qué o por qué no?
- ¿Qué debería hacer alguien si previamente ha insertado datos (por ejemplo, usando
NOW()
) sin preocuparse por el huso horario para asegurarse de que todo se mantenga constante? - Cualquier otra cosa que pienses que alguien debería tener en cuenta
Si es posible, intente separarlo en secciones lógicas para que sea más fácil para los futuros usuarios encontrar la información. Asegúrese de proporcionar ejemplos de código cuando sea necesario.
Esta respuesta se ha actualizado para acomodar la recompensa. La respuesta original no editada está debajo de la línea.
Casi todos los puntos de preguntas agregados por el propietario de la recompensa están relacionados con la forma en que las fechas de MySQL y PHP deberían interactuar, en el contexto de las zonas horarias.
MySQL todavía tiene un soporte de zona horaria patético , lo que significa que la inteligencia debe ser del lado de PHP.
- Establezca su zona horaria de conexión MySQL a UTC como se documenta en el enlace de arriba. Esto hará que todas las fechas manejadas por MySQL, incluido
NOW()
, sean manejadas de manera sana. - Siempre use
DATETIME
, nunca useTIMESTAMP
menos que requiera expresamente el comportamiento especial enTIMESTAMP
. Esto es menos doloroso de lo que solía ser.- Está bien almacenar el tiempo de época de Unix como un entero si es necesario, 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 PHP
Ymd H:i:s
- Convierta todas las fechas de PHP a UTC cuando las almacene en MySQL, lo cual es algo trivial como se describe a continuación.
- Las fechas y horas devueltas desde MySQL se pueden entregar de forma segura al constructor PHP DateTime. ¡Asegúrese de pasar en una zona horaria UTC también!
- Convierta PHP DateTime a la zona horaria local del usuario en echo , tan pronto como sea posible. Afortunadamente, la comparación de DateTime y las matemáticas con respecto a otros DateTimes tendrán en cuenta la zona horaria en la que cada uno se encuentra.
- Todavía está a la altura de la base de datos DST proporcionada con PHP. Mantenga sus parches de PHP y SO actualizados. Mantenga MySQL en el maravilloso estado de UTC para eliminar una posible molestia de DST.
Eso aborda la mayoría de los puntos.
Lo último es una tontería:
- ¿Qué debería hacer alguien si previamente ha insertado datos (por ejemplo, usando
NOW()
) sin preocuparse por el huso horario para asegurarse de que todo se mantenga constante?
Esto es una verdadera molestia. Una de las otras respuestas apuntaba a CONVERT_TZ
de MySQL, aunque personalmente lo hubiera hecho saltando entre las zonas horarias nativas del servidor y UTC durante las selecciones y actualizaciones, porque soy así de hardcore.
la aplicación también debería poder establecer / elegir el horario de verano en sí para cada usuario.
No necesita ni debe hacer esto en la era moderna.
Las versiones modernas de PHP tienen la clase DateTimeZone , que incluye la capacidad de enumerar zonas horarias con nombre . Las zonas horarias con nombre permiten al usuario seleccionar su ubicación real y hacer que el sistema determine automáticamente sus reglas de horario de verano en función de esa ubicación.
Puede combinar DateTimeZone con DateTime para obtener algunas funciones simples pero potentes. Simplemente puede almacenar y usar todas sus marcas de tiempo en UTC por defecto , y convertirlas a la zona horaria del usuario en exhibición.
// 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 usar esta técnica, el sistema seleccionará automáticamente las configuraciones de horario de verano correctas para el usuario, sin preguntar al usuario si están 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 listará las zonas y sus tiempos 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 alguna magia del lado del cliente. Javascript tiene una clase Date irregular pero funcional , 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 es el correcto.
Comparemos este método para hacerlo usted mismo. En realidad, necesitaría realizar cálculos matemáticos de fecha cada vez que manipule una fecha y hora, además de descartar una opción en el usuario que realmente no le interesará. Esto no es solo subóptimo, es un loco de guano de murciélago. Obligar a los usuarios a indicar cuándo quieren el soporte de DST es pedir problemas y confusión.
Además, si desea utilizar el marco de PHP DateTime y DateTimeZone moderno para esto, deberá usar cadenas de zona horaria Etc/GMT...
desuso en lugar de zonas horarias con nombre. Estos nombres de zona se pueden eliminar de futuras versiones de PHP, por lo que sería imprudente hacerlo. Digo todo esto por experiencia.
tl; dr : utilice el conjunto de herramientas moderno, evite los horrores de las matemáticas de fecha. Presente al usuario una lista de zonas horarias con nombre. Almacene sus fechas en UTC , que no se verán afectadas por el horario de verano de ninguna manera. Convierta las fechas en la zona horaria con nombre seleccionada del usuario en la pantalla , no antes.
Según lo solicitado, aquí hay un ciclo sobre las zonas horarias disponibles que muestran su desplazamiento GMT en minutos. Seleccioné minutos aquí para demostrar un hecho desafortunado: ¡no todas las compensaciones son en horas completas! Algunos en realidad cambian media hora de adelanto durante el horario de verano en lugar de una hora completa. El desplazamiento resultante en minutos debe coincidir 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";
}
Cómo almacenar los tiempos en la base de datos desde un objeto PHP
DateTime
El estándar SQL-92 especificó que los literales temporales deberían pasarse en SQL utilizando una palabra clave de tipo de datos adecuada (por ejemplo,
TIMESTAMP
para valores de fecha / hora) seguido de una representación de cadena del valor (que contiene un desfase horario opcional si no es predeterminado).Lamentablemente, MySQL no cumple con esta parte del estándar SQL. Como se documenta en Literales de fecha y hora :
El SQL estándar permite que los literales temporales se especifiquen utilizando una palabra clave de tipo y una cadena.
[ deletia ]
MySQL reconoce esas construcciones y también la sintaxis ODBC correspondiente:
[ deletia ]
Sin embargo, MySQL ignora la palabra clave type y cada una de las construcciones anteriores produce el valor de la cadena
'' str ''
, con un tipo deVARCHAR
.La documentación continúa describiendo los formatos literales compatibles con MySQL y, en particular, los offsets explícitos de la zona horaria están ausentes. Hay una solicitud de función para arreglar esto que ahora tiene más de siete años y que no parece que vaya a ser presentada pronto.
En su lugar, uno debe establecer la variable
time_zone
la sesión antes de intercambiar valores de fecha / hora entre el servidor y el cliente. Por lo tanto, usando PDO :Conéctese a MySQL:
$dbh = new PDO("mysql:dbname=$dbname", $username, $password); $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
Establezca la sesión
time_zone
a la del objetoDateTime
:$qry = $dbh->prepare(''SET SESSION time_zone = ?''); $qry->execute([$datetime->format(''P'')]);
Produzca un literal adecuado del objeto
DateTime
y pase a MySQL como normal (es decir, como un parámetro a una instrucción preparada).Como se describe en la documentación, hay varios formatos literales posibles que se pueden usar. Sin embargo, sugiero usar una cadena en
''YYYY-MM-DD hh:mm:ss.ffffff''
(tenga en cuenta que los segundos fraccionarios se ignorarán en las versiones de MySQL anteriores a la 5.6), ya que es el más cercano al SQL estándar; de hecho, uno podría prefijar el literal con la palabra claveTIMESTAMP
para garantizar que el SQL de uno sea portátil:$qry = $dbh->prepare('' UPDATE my_table SET the_time = TIMESTAMP ? WHERE ... ''); $qry->execute([$datetime->format(''Y-m-d H:i:s.u'')]);
¿Deberían almacenarse en
DATETIME
oTIMESTAMP
? ¿Cuáles son los beneficios o advertencias para cada uno?Los objetos
DateTime
PHP siempre se deben almacenar en columnas de tipoTIMESTAMP
.La diferencia más fundamental es que
TIMESTAMP
almacena información de zona horaria (almacenando el valor en UTC y convirtiendo a / desde según lo requerido por la variabletime_zone
anterior), mientras queDATETIME
no lo hace. Por lo tanto,TIMESTAMP
es útil para representar un momento específico en el tiempo (análogo a los objetosDateTime
PHP), mientras queDATETIME
es útil para representar el tiempo que se ve en un calendario / reloj (como en una foto).Como se documenta en los tipos
DATE
,DATETIME
yTIMESTAMP
:El tipo
DATETIME
se usa para valores que contienen partes de fecha y hora. MySQL recupera y muestra los valoresDATETIME
en''YYYY-MM-DD HH:MM:SS''
. El rango admitido es''1000-01-01 00:00:00''
a''9999-12-31 23:59:59''
.El tipo de datos
TIMESTAMP
se usa para valores que contienen partes de fecha y hora.TIMESTAMP
tiene un rango de''1970-01-01 00:00:01''
UTC a''2038-01-19 03:14:07''
UTC.MySQL convierte los valores de
TIMESTAMP
de la zona horaria actual a UTC para el almacenamiento y de regreso de UTC a la zona horaria actual para su recuperación. (Esto no ocurre con otros tipos, comoDATETIME
.) De forma predeterminada, la zona horaria actual para cada conexión es la hora del servidor. La zona horaria se puede configurar por conexión. Siempre que la configuración de zona horaria permanezca constante, obtendrá el mismo valor que almacena. Si almacena un valorTIMESTAMP
, y luego cambia la zona horaria y recupera el valor, el valor recuperado es diferente del valor almacenado. Esto ocurre porque la misma zona horaria no se usó para la conversión en ambas direcciones. La zona horaria actual está disponible como el valor de latime_zone
sistematime_zone
. Para obtener más información, consulte la Sección 10.6, "Soporte de zona horaria del servidor MySQL" .El tipo de datos
TIMESTAMP
ofrece inicialización automática y actualización a la fecha y hora actuales. Para obtener más información, consulte la Sección 11.3.5, "Inicialización y actualización automática paraTIMESTAMP
" .Tenga en cuenta el párrafo final, que a menudo atrapa a los recién llegados a MySQL.
También vale la pena agregar que, como se documenta en Requisitos de almacenamiento de tipo de datos , los valores
DATETIME
requieren 8 bytes para el almacenamiento, mientras que los valoresTIMESTAMP
solo requieren 4 bytes (el formato de almacenamiento de datos subyacente se puede encontrar en Representación del tipo de datos de fecha y hora ).¿Alguna vez tenemos que preocuparnos por las zonas horarias con
DATE
MySQL?Solo es significativo por un tiempo ser sensible a la zona horaria. Por definición, una fecha sola es universalmente igual independientemente de la zona horaria de cada uno y, por lo tanto, no es necesario "preocuparse por las zonas horarias" cuando se utiliza el tipo de datos
DATE
de MySQL.El corolario de esto es que, si uno tiene un valor que es sensible a la zona horaria, también debe almacenar su tiempo, por ejemplo, en una columna
TIMESTAMP
: el uso de una columnaDATE
provoca la pérdida irreversible de información significativa.Cómo insertar valores usando
NOW()
. ¿Es necesario convertir estos de alguna manera antes o después del inserto?Como se documenta en
NOW()
:Devuelve la fecha y la hora actuales como un valor en
''YYYY-MM-DD HH:MM:SS''
oYYYYMMDDHHMMSS.uuuuuu
, dependiendo de si la función se utiliza en una cadena o en un contexto numérico. El valor se expresa en la zona horaria actual.Como " el valor se expresa en la zona horaria actual " y esa misma " zona horaria actual " se utilizará para evaluar los valores de fecha / hora, no es necesario preocuparse por la zona horaria cuando se utiliza la función
NOW()
MySQL (o cualquiera de sus alias). Por lo tanto, para insertar un registro:INSERT INTO my_table (the_time) VALUES (NOW());
Tenga en cuenta que, como se mencionó anteriormente, la inicialización automática de MySQL de las columnas
TIMESTAMP
hace que la mayoría de los intentos redundantes en usarNOW()
sean redundantes durante la inserción / actualización de registros.¿Es necesario establecer el huso horario utilizado por MySQL? ¿Si es así, cómo? ¿Debería hacerse de forma persistente o en cada solicitud HTTP? ¿Tiene que establecerse en UTC o puede ser algo más? ¿O es suficiente el tiempo del servidor?
Esto ya se trató anteriormente. Uno puede establecer la variable
time_zone
de MySQL globalmente, si así lo desea y así evitar tener que configurarlo en cada conexión. Consulte Soporte de zona horaria del servidor MySQL para obtener más información.Cómo recuperar valores de MySQL y convertirlos a un objeto
DateTime
. ¿Bastará con ponerlo enDateTime::__construct()
o necesitamos usarDateTime::createFromFormat()
?Como se documenta en Formatos compuestos , uno de los formatos de fecha / hora reconocidos por el analizador que PHP usa en
DateTime::__construct()
es el formato de salida de MySQL.Sin embargo, dado que el formato de salida MySQL no incluye la zona horaria, uno debe asegurarse de proporcionar el constructor
DateTime
con esa información a través de su segundo argumento opcional:$qry = $dbh->prepare(''SET SESSION time_zone = ?''); $qry->execute([$timezone->getName()]); $qry = $dbh->query(''SELECT the_time FROM my_table''); $datetime = new DateTime($qry->fetchColumn(), $timezone);
Alternativamente, uno puede hacer que MySQL convierta la hora en una marca de tiempo de UNIX y construir el objeto
DateTime
partir de eso:$qry = $dbh->query(''SELECT UNIX_TIMESTAMP(the_time) FROM my_table''); $datetime = new DateTime($qry->fetchColumn());
Cuándo convertir a la hora local y por qué. ¿Hay algún momento en el que quisiéramos convertirlo antes de devolverlo al usuario (por ejemplo, compararlo con otro objeto DateTime o un valor estático)?
No estoy seguro de lo que quiere decir con "hora local" (¿local para quién? ¿RDBMS ?, ¿servidor web? ¿Cliente web?), Pero las comparaciones entre objetos
DateTime
manejarán las conversiones de zona horaria según sea necesario (PHP almacena los valores internamente en UTC y solo convierte para la salida).¿Alguna vez tenemos que preocuparnos por el horario de verano (DST)? ¿Por qué o por qué no?
En términos generales, si sigue la metodología anterior, la única preocupación del horario de verano es cuando se garantiza que los valores se representan al usuario en la zona horaria que esperan.
¿Qué debería hacer alguien si previamente ha insertado datos (por ejemplo, usando
NOW()
) sin preocuparse por el huso horario para asegurarse de que todo se mantenga constante?Como se mencionó anteriormente, el uso de
NOW()
nunca debería causar problemas.Si se han insertado valores literales en una columna
TIMESTAMP
mientras la variabletime_zone
la sesión se estableció en un valor incorrecto, será necesario actualizar esos valores en consecuencia. La funciónCONVERT_TZ()
MySQL puede resultar útil:UPDATE my_table SET the_time = CONVERT_TZ(the_time, ''+00:00'', ''+10:00'');