Parece haber bastante confusión sobre cómo funcionan realmente las marcas de tiempo, las fechas y las zonas horarias. Este artículo tiene como objetivo desmitificar estos conceptos y ofrecer algunas recomendaciones y mejores prácticas sobre cómo gestionar las fechas y las zonas horarias de forma sensata en tu aplicación Laravel y MySQL.
Cómo funciona el tipo TIMESTAMP en MySQL
La documentación oficial de MySQL lo explica de la siguiente manera:
MySQL convierte los valores TIMESTAMP de la zona horaria actual a UTC para su almacenamiento y de vuelta de UTC a la zona horaria actual para su recuperación. (Esto no ocurre con otros tipos, como DATETIME). De forma predeterminada, la zona horaria actual de cada conexión es la hora del servidor. La zona horaria se puede configurar para cada conexión. Mientras la configuración de la zona horaria permanezca constante, recuperarás el mismo valor que almacenaste. Si guardas un valor de TIMESTAMP y, a continuación, cambias la zona horaria y recuperas el valor, el valor recuperado es diferente del valor que almacenaste. Esto ocurre porque no se usó la misma zona horaria para la conversión en ambas direcciones. La zona horaria actual está disponible como el valor de zona_hora variable de sistema. Para obtener más información, consulte Sección 5.1.15, «Compatibilidad con zonas horarias del servidor MySQL».
Esta explicación es quizás un poco abstracta. Por lo tanto, agreguemos un poco de contexto y veamos lo que realmente sucede entre bastidores.
Zona horaria actual
Para entender cómo funcionan las conversiones de marcas de tiempo, primero necesitamos saber qué se entiende por zona horaria actual.
En resumen, zona horaria actual es el valor de la zona horaria de la SESIÓN. De forma predeterminada, es la hora del SISTEMA del servidor en el que se ejecuta la base de datos. Realicemos algunas consultas para ilustrar esto.
Corriendo SELECCIONA @ @SESSION .time_zone;
devolverá la SESSSION time_zone actual de la siguiente manera:
+---------------------+
| @ @SESSION .time_zone |
+---------------------+
| SISTEMA |
+---------------------+
Cambiemos la zona horaria de la SESIÓN a «+ 02:00» ejecutando: SET SESSION time_zone = '+ 02:00 ';
SELECCIONA @ @SESSION .time_zone;
ahora devolverá:
+---------------------+
| @ @SESSION .time_zone |
+---------------------+
| + 02:00 |
+---------------------+
Ejemplos prácticos de cómo funciona Timestamp
Veamos ahora algunos ejemplos con fechas y horas específicas para ver cómo funciona el almacenamiento y la recuperación de marcas de tiempo en la vida real.
Empezaremos creando una tabla con una columna TIMESTAMP para almacenar nuestros datos de prueba.
CREAR TABLA timestamp_test (
`marca de tiempo` MARCA DE TIEMPO,
);
Ahora configuraremos nuestra sesión time_zone en + 02:00 y almacenaremos algunos datos
SET SESSION time_zone = '+ 02:00 ';
INSERTAR EN LOS VALORES DE timestamp_test ('1970-01-01 03:00:00 ');
Comprueba que el valor esté almacenado:
SELECCIONA * DE timestamp_test;
Vamos a ver:
+---------------------+
| marca de tiempo |
+---------------------+
| 1970-01-01 03:00:00 |
+---------------------+
Representación esquemática del proceso de almacenamiento:

Hasta ahora todo va bien. Pero, ¿qué pasa si cambiamos la zona horaria de la sesión?
Vamos a configurar nuestra sesión time_zone en + 00:00
y vuelva a recuperar los datos.
SET SESSION time_zone = '+ 00:00 ';
SELECCIONA * DE timestamp_test;
Vamos a ver:
+---------------------+
| marca de tiempo |
+---------------------+
| 1970-01-01 01:00:00 |
+---------------------+
Representación esquemática del proceso de recuperación:

Conclusiones clave:
- MySQL almacena el valor de la marca de tiempo como Marca de tiempo de Unix en segundos.
- MySQL sí no almacena cualquier información sobre la zona horaria.
- Cada vez que almacena un valor como marca de tiempo, se convierte en la marca de tiempo de Unix de acuerdo con la zona horaria de la sesión actual.
- Cada vez que recuperas una marca de tiempo, se convierte en el valor de fecha y hora de acuerdo con la zona horaria de la sesión actual.
Nota: Un algoritmo simple para convertir fechas en marcas de tiempo de acuerdo con una zona horaria específica podría tener este aspecto (en caso de que te interese cómo se hace realmente):
- Obtenga la diferencia entre la fecha y hora y la época de Unix (1970-01-01 00:00:00) en segundos.
- Convierte tu compensación de zona horaria actual en segundos
- Resta tu compensación de zona horaria actual del valor que obtuviste en el paso 1.
Por ejemplo, supongamos que nuestro desfase de zona horaria es + 02:00 y queremos convertir 1970-01-01 03:00:00 en una marca de tiempo de Unix.
- 1970-01-01 03:00:00 - 1970-01-01 00:00:00 = 3h = 3* 60 * 60 = 10800
- + 02:00 en segundos es: 2 * 60 * 60 = 7200
- 10800 - 7200 = 3600
Otro ejemplo, supongamos que nuestra compensación de zona horaria es - 03:00 y queremos convertir 1970-01-01 08:00:00 en una marca de tiempo de Unix.
- 1970-01-01 08:00:00 - 1970-01-01 00:00:00 = 8h = 8* 60 * 60 = 28800
- - 03:00 en segundos es: -3 * 60 * 60 = -10800
- 28800 - - 10800 = 39600 (tenga en cuenta que en realidad sumamos los valores cuando las dobles menos dan un +)
En qué se diferencia el tipo TIMESTAMP de los tipos DATE y DATETIME
En el caso de TIMESTAMP, el valor real que se almacena y se recupera depende de la zona horaria de la sesión, mientras que DATE y DATETIME siempre se recuperan exactamente como los mismos valores que se almacenaron. Puede imaginar los valores DATE y DATETIME como cadenas estáticas. La cadena que almacena no cambia al recuperarla. Siempre recuperará exactamente el mismo valor que almacenó sin importar la zona horaria de la base de datos o de la sesión.
TIMESTAMP solo puede almacenar valores del 1970-01-01 00:00:00 al 2038-01-19 03:14:17. La razón de esto es cómo se codifica la hora de Unix: https://en.wikipedia.org/wiki/Year_2038_problem
DATETIME y DATE no tienen ese límite.
Cómo maneja Laravel las fechas y horas
Hemos visto cómo funciona la marca de tiempo por parte de MySQL. Veamos ahora cómo Laravel maneja las fechas y horas.
Usos de Laravel Carbono para generar fechas (https://laravel.com/docs/10.x/helpers#dates). Carbon, a su vez, utiliza las funciones de fecha/hora de PHP. https://www.php.net/manual/en/ref.datetime.php. Esto significa que cuando generamos una fecha actual, lo hacemos de acuerdo con la zona horaria de PHP. Pero, ¿qué determina la zona horaria de PHP? Bueno, Laravel lo hace convenientemente por ti a través de la configuración de zona horaria de config/app.php.
¿Qué tipo de implicaciones tiene lo anterior en la forma en que se guardan las fechas en nuestra base de datos? Podemos poner un ejemplo para ilustrar esto.
Consideremos la siguiente situación:
- la zona horaria en el archivo config/app.php de nuestra aplicación está configurada en
Europa/Berlín
- nuestra sesión de base de datos time_zone es
Europa/Tallin
La configuración mysql.timezone en config/database.php. Si no lo configura específicamente, es probable que la base de datos utilice la hora del sistema del servidor en el que se ejecuta.
1. Generamos una fecha en nuestra aplicación Laravel usando el ahora ()
función auxiliar que nos devuelve la siguiente fecha: «2023-10-13 16:00:00». Esta es la fecha y hora actual en Europa/Berlín
2. Luego enviamos el mensaje «2023-10-13 16:00:00» a nuestra base de datos MySQL para almacenarlo en una columna de fecha y hora (por ejemplo, creando un modelo y llamando a save () en él)
3. Nuestra base de datos toma «2023-10-13 16:00:00» y lo convierte en una marca de tiempo de Unix de acuerdo con Europa/Tallin
zona horaria y, a continuación, la almacena. ¿Te das cuenta de lo que está pasando aquí? Generamos la fecha y hora de acuerdo con Europa/Berlín
pero nuestra base de datos lo convirtió en una marca de tiempo según Europa/Tallin
4. Cuando recuperamos la marca de tiempo, nuestra base de datos convierte la marca de tiempo a fecha y hora de acuerdo con Europa/Tallin
(zona_hora de la sesión). El resultado es «2023-10-13 16:00:00» (la fecha y hora original que generamos). De un vistazo, todo parece estar bien. Sin embargo, ¿qué sucede si cambiamos la zona horaria de nuestra aplicación para que también sea Europa/Tallin
?
5. En cuanto a la recuperación, nada cambia. Todavía estamos a las 16:00:00 del 13 de octubre de 2023, ya que la conversión depende de la zona horaria de la sesión de la base de datos y no de la zona horaria de nuestra aplicación.
6. Los problemas reales surgen cuando comenzamos a hacer comparaciones de fechas en nuestra aplicación. Supongamos que la fecha que guardamos originalmente era la fecha de creación de un token y han pasado 30 minutos desde que lo generamos. Ahora queremos ver si el token ha caducado. Para eso:
- obtenemos nuestra hora actual con
ahora ()
(que ahora genera fechas segúnEuropa/Tallin
zona horaria (ya que cambiamos la zona horaria de nuestra aplicación), obtenemos el 13-10-2022 17:30:00 - obtenemos la hora de creación del token de la base de datos: 13-10-2022 16:00:00
- el token debería ser válido durante 1 hora, por lo que restamos la fecha de creación de la hora actual y obtenemos una diferencia de 1,5 h, lo que parece indicar que el token ha caducado. Sin embargo, en realidad, solo han pasado 30 minutos.
Conclusiones clave y mejores prácticas
Puede parecer que ejecutar la base de datos y la aplicación Laravel en diferentes zonas horarias es bastante seguro si nunca cambias la configuración de la zona horaria. Sin embargo, es una apuesta arriesgada.
Los cambios de zona horaria pueden producirse fácilmente si ejecutas muchas instancias de tus aplicaciones y bases de datos. La mayoría de los proveedores de nube configuran las zonas horarias de sus instancias en UTC de forma predeterminada, por lo que si utilizas una zona horaria diferente, debes tener mucho cuidado de configurar siempre las instancias en esa zona horaria específica.
También tendrás que tener en cuenta el horario de verano. Por ejemplo, si la zona horaria de la sesión de la base de datos es UTC y la zona horaria de la aplicación es Europa/Berlín
entonces terminarás con una plétora de problemas el último domingo de octubre, cuando la diferencia de Europa/Berlín
cambios debidos al cambio de horario de verano.
Considerando todo esto, la forma más sensata de manejar las fechas en Laravel y MySQL es la siguiente:
- Configura siempre las zonas horarias de tus aplicaciones y bases de datos en UTC. De esta forma, no tendrás que lidiar con ningún problema de conversión o zona horaria.
- Si desea mostrar las fechas según la zona horaria de su usuario final, convierta la fecha en la zona horaria del usuario final justo antes de mostrarla. Evita guardarla en una zona horaria diferente.
En cuanto a si usar DATETIME o TIMESTAMP, esa decisión depende de usted y depende del caso de uso.