- 1 1. La consulta SQL más corta para obtener la fecha/hora actual en MySQL
- 1.1 1.1 Obtener la fecha/hora actual (básico)
- 1.2 1.2 Obtener solo la fecha de hoy
- 1.3 1.3 Obtener solo la hora actual
- 1.4 1.4 Obtener la hora actual en UTC (importante)
- 1.5 1.5 Obtener milisegundos / microsegundos
- 1.6 1.6 Mejor función según el caso de uso (comienza aquí si no estás seguro)
- 1.7 Puntos clave que la gente suele pasar por alto
- 2 2. Diferencias entre NOW() / CURRENT_TIMESTAMP / SYSDATE()
- 3 3. Cambiar el formato de visualización de la fecha/hora actual
- 4 4. Cálculos de datetime usando la hora actual
- 4.1 4.1 Añadir/restar con INTERVAL
- 4.2 4.2 Obtener datos de las últimas 24 horas (patrón más común)
- 4.3 4.3 Obtener diferencias de días con DATEDIFF()
- 4.4 4.4 Usar TIMESTAMPDIFF() para horas/minutos/segundos
- 4.5 4.5 Obtener inicio de mes / fin de mes (trampa común en el mundo real)
- 4.6 Resumen de errores comunes
- 4.7 Puntos clave de esta sección
- 5 5. Para consultas de rango, esto es más seguro que BETWEEN
- 5.1 5.1 Conceptos básicos de BETWEEN y la trampa
- 5.2 5.2 Patrón más seguro (recomendado)
- 5.3 5.3 La forma correcta de obtener los datos de hoy
- 5.4 5.4 Patrones seguros para los últimos 7 / últimos 30 días
- 5.5 5.5 Regla clave para mantener los índices efectivos
- 5.6 Resumen de errores comunes
- 5.7 Lecciones clave de esta sección
- 6 6. DEFAULT CURRENT_TIMESTAMP y ON UPDATE (básicos del diseño de tablas)
- 6.1 6.1 Configuración automática de created_at (DEFAULT CURRENT_TIMESTAMP)
- 6.2 6.2 Actualización automática de updated_at (ON UPDATE)
- 6.3 6.3 DATETIME vs TIMESTAMP (importante)
- 6.4 6.4 CURRENT_TIMESTAMP también se puede usar con DATETIME
- 6.5 6.5 Si quieres usar NOW(): una alternativa (trigger)
- 6.6 Errores de diseño comunes
- 6.7 Lecciones clave de esta sección
- 7 7. Diseño de zonas horarias (almacenar en UTC, mostrar en hora local)
- 7.1 7.1 Verificar la zona horaria actual
- 7.2 7.2 Cambiar la zona horaria por sesión
- 7.3 7.3 Por qué almacenar en UTC (principio del mundo real)
- 7.4 7.4 Convertir zonas horarias con CONVERT_TZ()
- 7.5 7.5 Por qué CONVERT_TZ() devuelve NULL
- 7.6 7.6 Cambiar la zona horaria a nivel de servidor
- 7.7 Errores comunes
- 7.8 Lecciones clave de esta sección
- 8 8. Ejemplos prácticos que puedes usar tal cual
- 8.1 8.1 Insertar automáticamente la hora actual en registros
- 8.2 8.2 Actualizar la hora del último inicio de sesión
- 8.3 8.3 Fijar una hora de referencia en trabajos por lotes
- 8.4 8.4 Agregación para hoy / ayer / últimos 7 días
- 8.5 8.5 Verificaciones de expiración (sesiones/tokenes)
- 8.6 8.6 Obtener filas que expiran dentro de N horas
- 8.7 Lo que siempre debes tener en cuenta
- 8.8 Puntos clave de esta sección
- 9 9. Preguntas Frecuentes (FAQ)
- 9.1 9.1 ¿Cuál es la diferencia entre NOW() y CURRENT_TIMESTAMP?
- 9.2 9.2 ¿Debería usar SYSDATE()?
- 9.3 9.3 ¿Por qué se desplaza la hora?
- 9.4 9.4 CONVERT_TZ() devuelve NULL
- 9.5 9.5 Desplazamientos de rango con BETWEEN
- 9.6 9.6 ¿Cómo elegir entre DATETIME y TIMESTAMP?
- 9.7 9.7 No se están almacenando microsegundos
- 9.8 Lecciones clave de esta sección
- 10 10. Resumen
1. La consulta SQL más corta para obtener la fecha/hora actual en MySQL
Si deseas obtener la fecha/hora actual en MySQL, las primeras funciones que debes recordar son NOW() y CURRENT_TIMESTAMP. A continuación se presentan los ejemplos SQL más cortos según el caso de uso.
1.1 Obtener la fecha/hora actual (básico)
SELECT NOW();
or
SELECT CURRENT_TIMESTAMP;
- Ambas devuelven la fecha/hora actual (YYYY-MM-DD HH:MM:SS).
- Dentro de la misma consulta, la hora se fija en el “momento de inicio de la consulta”.
Casos de uso
- Registro de eventos
- Obtener la marca de tiempo de creación de un registro
- Obtener una hora de referencia
Error común
- La zona horaria de la aplicación y la zona horaria de la base de datos difieren y se produce un “desplazamiento de tiempo”. → Verifica con
SELECT @@session.time_zone;
1.2 Obtener solo la fecha de hoy
SELECT CURDATE();
- Formato de retorno:
YYYY-MM-DD - El tipo es DATE (solo fecha, no datetime)
Casos de uso
- Buscar los datos de “hoy”
- Agregación diaria
Notas
- A diferencia de
NOW(), no incluye la hora. - Si necesitas la hora para consultas de rango, usa
NOW().
1.3 Obtener solo la hora actual
SELECT CURTIME();
- Formato de retorno:
HH:MM:SS - El tipo es TIME (solo hora)
Casos de uso
- Mostrar el tiempo de ejecución de un lote
- Imprimir solo la parte de hora en los registros
Trampa común
- Debido a que no incluye una fecha, no puedes usarlo para comparaciones datetime.
1.4 Obtener la hora actual en UTC (importante)
SELECT UTC_TIMESTAMP();
- Devuelve UTC sin importar la zona horaria del servidor.
- Recomendado para servicios globales.
Política base en proyectos reales
- Almacenar en UTC
- Convertir a hora local al mostrar
Notas
- Si la aplicación ya convierte a UTC, ten cuidado con la doble conversión.
1.5 Obtener milisegundos / microsegundos
SELECT NOW(3); -- milliseconds (3 digits after the decimal point)
SELECT NOW(6); -- microseconds (6 digits after the decimal point)
- Disponible en MySQL 5.6.4 y posteriores.
- Utilizado para registros de alta precisión y auditoría de transacciones.
Idea errónea común
NOW()por sí solo no puede devolver segundos fraccionarios.- Tu columna también debe especificar precisión, por ejemplo
DATETIME(6).
1.6 Mejor función según el caso de uso (comienza aquí si no estás seguro)
| Use case | Recommended function |
|---|---|
| General current datetime | NOW() |
| Table default value | CURRENT_TIMESTAMP |
| Date only | CURDATE() |
| Time only | CURTIME() |
| Unified UTC management | UTC_TIMESTAMP() |
| High-precision logs | NOW(6) |
Puntos clave que la gente suele pasar por alto
NOW()yCURRENT_TIMESTAMPson efectivamente equivalentes (mismo valor)- Dentro de la misma consulta, la hora está fija
- El valor mostrado cambia según la configuración de zona horaria
- Para microsegundos, la definición de tu columna debe especificar precisión
2. Diferencias entre NOW() / CURRENT_TIMESTAMP / SYSDATE()
Existen varias funciones para obtener la hora actual, pero la parte más confusa es la diferencia entre NOW(), CURRENT_TIMESTAMP y SYSDATE(). Si no comprendes esto correctamente, puedes obtener comportamientos inesperados en los registros y en el procesamiento de transacciones.
2.1 NOW() y CURRENT_TIMESTAMP son efectivamente equivalentes
SELECT NOW(), CURRENT_TIMESTAMP;
- Ambas devuelven el datetime actual (un valor DATETIME).
- Dentro de la misma consulta, la hora se fija en el “momento de inicio de la consulta”.
CURRENT_TIMESTAMPtambién puede usarse con sintaxis de llamada a función:SELECT CURRENT_TIMESTAMP();
Cómo elegir en proyectos reales
| Use case | Recommended |
|---|---|
| Simple retrieval | NOW() |
| Table DEFAULT / ON UPDATE | CURRENT_TIMESTAMP |
Puntos importantes
- El significado del valor obtenido es el mismo.
- La principal diferencia es la sintaxis/uso (p.ej., permitido en DEFAULT).
- No es una diferencia de tipo (fácil de malinterpretar).
2.2 SYSDATE() devuelve la hora en el “momento de evaluación”
SYSDATE() se parece a NOW(), pero el momento en que el valor se fija es diferente.
SELECT NOW(), SLEEP(2), NOW();
Resultado (ejemplo):
2025-02-23 14:00:00
2025-02-23 14:00:00
SELECT SYSDATE(), SLEEP(2), SYSDATE();
Resultado (ejemplo):
2025-02-23 14:00:00
2025-02-23 14:00:02
La diferencia esencial
| Function | When the time is fixed |
|---|---|
NOW() | Query start time |
SYSDATE() | Evaluation time |
2.3 Notas para transacciones y replicación
Debido a que NOW() se fija al inicio de la consulta, es
más seguro cuando necesitas un tiempo de referencia consistente dentro de una transacción.
Por otro lado, dado que SYSDATE() depende del momento de ejecución, puede afectar la reproducibilidad en:
- Replicación
- Trabajos por lotes
- Procesamiento masivo de registros
Regla general
- Quieres un tiempo de referencia fijo →
NOW() - Necesitas el instante exacto en todo momento →
SYSDATE()(uso limitado)
2.4 Nota: CURRENT_DATE / CURRENT_TIME / LOCALTIME
MySQL también soporta:
SELECT CURRENT_DATE;
SELECT CURRENT_TIME;
SELECT LOCALTIME;
CURRENT_DATE→ lo mismo queCURDATE()CURRENT_TIME→ lo mismo queCURTIME()LOCALTIME→ lo mismo queNOW()
Confusión común
- Estas son mayormente “diferencias cosméticas”; hay poca diferencia funcional.
- Por seguridad, prioriza la legibilidad y estandariza dentro del proyecto.
Puntos clave de esta sección
NOW()yCURRENT_TIMESTAMPsignifican esencialmente lo mismo.SYSDATE()se comporta de manera diferente porque se basa en el tiempo de evaluación.- Para tiempos de referencia fijos, usa
NOW()por defecto. - Usa
CURRENT_TIMESTAMPpara DEFAULT y ON UPDATE.
3. Cambiar el formato de visualización de la fecha/hora actual
El tiempo actual que obtienes con NOW() se muestra por defecto como YYYY-MM-DD HH:MM:SS. En el trabajo real, a menudo necesitarás cosas como:
- Mostrar solo la fecha
- Usar
YYYY/MM/DD - Usar un formato localizado (p.ej., 23 de feb. de 2025)
- Mostrar milisegundos
Para esto, usa DATE_FORMAT() (convierte un datetime a un formato de cadena arbitrario).
3.1 Sintaxis básica de DATE_FORMAT()
DATE_FORMAT(datetime, 'format_string')
Ejemplo: Convertir el tiempo actual a YYYY/MM/DD HH:MM
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d %H:%i');
Ejemplo de salida:
2025/02/23 14:35
Especificadores de formato comunes
| Specifier | Meaning | Example |
|---|---|---|
%Y | 4-digit year | 2025 |
%m | 2-digit month | 02 |
%d | 2-digit day | 23 |
%H | Hour (24-hour) | 14 |
%i | Minutes | 35 |
%s | Seconds | 50 |
%f | Microseconds | 123456 |
Notas
- Algunos especificadores difieren entre mayúsculas y minúsculas.
%hes hora en formato de 12 horas,%Hes hora en formato de 24 horas.
3.2 Ejemplos de formatos comunes (frecuentes en el trabajo real)
1. Separado por barras
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d');
2. Localizado (Inglés)
SELECT DATE_FORMAT(NOW(), '%b %d, %Y');
3. Sin segundos
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i');
Trampa común
- Debido a que devuelve una cadena, no puedes usarlo para cálculos numéricos.
- Para comparaciones y búsquedas, deberías usar el tipo DATETIME original.
3.3 Extraer solo la parte de fecha o hora
También puedes obtener partes manteniendo el tipo (en lugar de formatear a una cadena).
SELECT DATE(NOW()); -- date only (DATE type)
SELECT TIME(NOW()); -- time only (TIME type)
SELECT YEAR(NOW()); -- year only
SELECT MONTH(NOW()); -- month only
SELECT DAY(NOW()); -- day only
Uso recomendado
| Goal | Recommended |
|---|---|
| Formatting for display | DATE_FORMAT |
| Calculation / comparison | DATE() / TIME() |
3.4 Mostrar con microsegundos
Cuando necesitas registros de alta precisión:
SELECT NOW(6);
Con formato:
SELECT DATE_FORMAT(NOW(6), '%Y-%m-%d %H:%i:%s.%f');
Notas importantes
- Tu columna debe ser
DATETIME(6)oTIMESTAMP(6), de lo contrario la precisión no se almacenará. - No disponible en versiones de MySQL anteriores a la 5.6.4 (dependiendo del entorno).
Errores comunes
Usar
DATE_FORMAT()en una cláusula WHERE impide que se usen índices. wp:list /wp:list- Mal ejemplo:
WHERE DATE_FORMAT(created_at, '%Y-%m-%d') = '2025-02-23' - Recomendado:
WHERE created_at >= '2025-02-23 00:00:00' AND created_at < '2025-02-24 00:00:00' - Almacenar directamente la cadena con formato de visualización (daña el rendimiento de búsqueda)
- Mal ejemplo:
Puntos clave de esta sección
- Usa
DATE_FORMAT()para cambiar los formatos de visualización. - Para cálculos/comparaciones, mantén el tipo original.
- Si necesitas microsegundos, especifica la precisión en la definición de la columna.
- Usar funciones en cláusulas WHERE puede causar problemas de rendimiento.
4. Cálculos de datetime usando la hora actual
En MySQL, es común calcular cosas como “N horas después”, “N días atrás” o “los últimos N días” basándose en la hora actual.
El caso real más frecuente es “obtener datos de las últimas 24 horas.”
La base de la aritmética de fechas y horas es INTERVAL (sintaxis para añadir/restar unidades de tiempo).
4.1 Añadir/restar con INTERVAL
Obtener 1 hora después
SELECT NOW() + INTERVAL 1 HOUR;
Obtener 3 días atrás
SELECT NOW() - INTERVAL 3 DAY;
Obtener 1 mes después
SELECT NOW() + INTERVAL 1 MONTH;
Unidades comunes
| Unit | Meaning |
|---|---|
| SECOND | Seconds |
| MINUTE | Minutes |
| HOUR | Hours |
| DAY | Days |
| WEEK | Weeks |
| MONTH | Months |
| YEAR | Years |
Errores comunes
DAYenINTERVAL 1 DAYfunciona incluso en minúsculas, pero deberías estandarizarlo dentro del equipo.- Los cálculos al final del mes pueden diferir de lo esperado porque el número de días varía (p. ej., 31 de enero + 1 mes).
4.2 Obtener datos de las últimas 24 horas (patrón más común)
SELECT *
FROM users
WHERE created_at >= NOW() - INTERVAL 1 DAY;
Significado
- Apunta a “desde ahora hasta hace 24 horas”.
Notas
- Si utilizas
CURDATE(), la referencia pasa a ser “hoy a 00:00”, lo que cambia el significado.
Ejemplo (datos de hoy):
SELECT *
FROM users
WHERE created_at >= CURDATE();
Entender la diferencia es crucial
| Expression | Meaning |
|---|---|
NOW() - INTERVAL 1 DAY | Past 24 hours |
CURDATE() | Since today 00:00 |
4.3 Obtener diferencias de días con DATEDIFF()
SELECT DATEDIFF('2025-03-01', '2025-02-23');
Resultado:
6
- La unidad es “días”
- La parte de tiempo se ignora
Notas
- El signo cambia según el orden de los argumentos.
- No puedes calcular diferencias en horas/minutos/segundos.
4.4 Usar TIMESTAMPDIFF() para horas/minutos/segundos
SELECT TIMESTAMPDIFF(HOUR,
'2025-02-23 12:00:00',
'2025-02-23 18:30:00');
Resultado:
6
Minutos
SELECT TIMESTAMPDIFF(MINUTE,
'2025-02-23 12:00:00',
'2025-02-23 12:30:00');
Segundos
SELECT TIMESTAMPDIFF(SECOND,
'2025-02-23 12:00:00',
'2025-02-23 12:00:45');
Casos de uso
- Cálculos de duración de sesiones
- Verificaciones de expiración
- Decisiones de tiempo de espera
4.5 Obtener inicio de mes / fin de mes (trampa común en el mundo real)
Obtener el primer día de este mes:
SELECT DATE_FORMAT(NOW(), '%Y-%m-01');
Primer día del próximo mes:
SELECT DATE_FORMAT(NOW() + INTERVAL 1 MONTH, '%Y-%m-01');
Notas
- El fin de mes no es una fecha fija.
- Para consultas de rango, “>= inicio AND < primer día del próximo mes” es seguro.
Resumen de errores comunes
- Usar
BETWEENcon valores solo de fecha y olvidar la hora - Desactivar índices al usar
DATE(created_at)en cláusulas WHERE - Quedarse atrapado por el problema del “día 31” en cálculos de mes
- No comprender la diferencia entre
NOW()yCURDATE()
Puntos clave de esta sección
INTERVALes la base de la aritmética de fechas y horas.- Últimas 24 horas =
NOW() - INTERVAL 1 DAY. - Días =
DATEDIFF(); unidades menores =TIMESTAMPDIFF(). - Ten cuidado con las condiciones finales en cálculos basados en meses.
5. Para consultas de rango, esto es más seguro que BETWEEN
Al realizar búsquedas de rangos de fechas en MySQL, muchos principiantes usan BETWEEN. Sin embargo, a menudo produce resultados no deseados si manejas incorrectamente los límites de tiempo, por lo que se recomienda un patrón más seguro en el trabajo real.
5.1 Conceptos básicos de BETWEEN y la trampa
Una consulta común
SELECT *
FROM orders
WHERE order_date BETWEEN '2025-02-01' AND '2025-02-10';
Parece correcta, pero internamente es equivalente a:
WHERE order_date >= '2025-02-01 00:00:00'
AND order_date <= '2025-02-10 00:00:00'
Así que no incluye datos después de las 00:00 del 10 de febrero.
5.2 Patrón más seguro (recomendado)
Patrón recomendado
SELECT *
FROM orders
WHERE order_date >= '2025-02-01 00:00:00'
AND order_date < '2025-02-11 00:00:00';
Puntos clave
- Especifica el límite final como “menor que el siguiente día a las 00:00”
- Más seguro que
<= 23:59:59(soporta microsegundos)
5.3 La forma correcta de obtener los datos de hoy
Ejemplo malo:
WHERE DATE(created_at) = CURDATE();
Problemas:
- Aplicar una función a la columna desactiva los índices
- Puede causar ralentizaciones graves en tablas grandes
Recomendado:
WHERE created_at >= CURDATE()
AND created_at < CURDATE() + INTERVAL 1 DAY;
Esto asegura:
- Se pueden usar índices
- Búsquedas rápidas
- Sin problemas de límites
5.4 Patrones seguros para los últimos 7 / últimos 30 días
Últimos 7 días
WHERE created_at >= NOW() - INTERVAL 7 DAY;
Últimos 30 días
WHERE created_at >= NOW() - INTERVAL 30 DAY;
Notas
CURDATE() - INTERVAL 7 DAYse basa en “hoy 00:00”NOW() - INTERVAL 7 DAYse basa en “hora actual”- Elige según los requisitos
5.5 Regla clave para mantener los índices efectivos
Malo:
WHERE DATE(created_at) = '2025-02-23';
Bueno:
WHERE created_at >= '2025-02-23 00:00:00'
AND created_at < '2025-02-24 00:00:00';
Por qué:
- Los índices funcionan en “la columna misma”
- Aplicar una función impide el uso de índices (escaneo completo)
Resumen de errores comunes
- Olvidar incluir la hora en el límite final en
BETWEEN - Falta de microsegundos al usar
23:59:59 - Usar
DATE()en cláusulas WHERE y ralentizar consultas - Dejar ambiguo “ahora” vs “hoy 00:00”
Lecciones clave de esta sección
- Las consultas de rango son más seguras con
>= inicio AND < fin. BETWEENrequiere un manejo cuidadoso de los límites.- Para mantener los índices efectivos, no envuelvas las columnas en funciones.
- Elige claramente entre lógica basada en
NOW()y basada enCURDATE().
6. DEFAULT CURRENT_TIMESTAMP y ON UPDATE (básicos del diseño de tablas)
En el diseño de bases de datos, es común gestionar automáticamente created_at y updated_at.
En MySQL, CURRENT_TIMESTAMP te permite establecer la hora actual automáticamente en inserciones y actualizaciones.
6.1 Configuración automática de created_at (DEFAULT CURRENT_TIMESTAMP)
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Comportamiento:
INSERT INTO users (name) VALUES ('Alice');
→ La hora actual se inserta automáticamente en created_at.
Puntos clave
CURRENT_TIMESTAMPse puede usar en DEFAULT.NOW()no se puede usar directamente en DEFAULT.
Error común
created_at DATETIME DEFAULT NOW(); -- error
Razón:
- En MySQL, generalmente no puedes establecer una función como DEFAULT (la excepción es CURRENT_TIMESTAMP).
6.2 Actualización automática de updated_at (ON UPDATE)
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
);
Comportamiento:
UPDATE users SET name = 'Bob' WHERE id = 1;
→ updated_at se actualiza automáticamente a la hora actual.
Casos de uso
- Seguimiento del último inicio de sesión
- Historial de actualizaciones
- Registros de auditoría
6.3 DATETIME vs TIMESTAMP (importante)
| Type | Range | Time zone impact |
|---|---|---|
| DATETIME | Year 1000 to 9999 | No |
| TIMESTAMP | 1970 to 2038 | Yes |
La diferencia esencial
TIMESTAMPse almacena internamente en UTC y se convierte a la zona horaria de la sesión al mostrarlo.DATETIMEalmacena el valor literal (sin conversión).
Guías
| Case | Recommended type |
|---|---|
| Log management | TIMESTAMP |
| Future dates (after 2038) | DATETIME |
| Fixed values not requiring TZ conversion | DATETIME |
6.4 CURRENT_TIMESTAMP también se puede usar con DATETIME
En MySQL 5.6 y posteriores, puedes especificar CURRENT_TIMESTAMP como DEFAULT y ON UPDATE para columnas DATETIME también.
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
Notas
- Las versiones antiguas de MySQL tienen restricciones (dependientes del entorno).
- Si necesitas precisión:
DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6)
6.5 Si quieres usar NOW(): una alternativa (trigger)
CREATE TRIGGER set_created_at
BEFORE INSERT ON logs
FOR EACH ROW
SET NEW.created_at = NOW();
Casos de uso
- Lógica compleja de valores iniciales
- Asignación condicional de marcas de tiempo
Notas
- Los desencadenadores son difíciles de depurar.
- Evítalos a menos que sea necesario.
Errores de diseño comunes
- Agregar ON UPDATE a tanto
created_atcomoupdated_at - Confundir el comportamiento de DATETIME y TIMESTAMP
- Postergar decisiones sobre zonas horarias
Lecciones clave de esta sección
- Usa
CURRENT_TIMESTAMPpara DEFAULT. - Usa
ON UPDATE CURRENT_TIMESTAMPpara el seguimiento de actualizaciones. - Elige tipos según el comportamiento de zonas horarias y el problema del año 2038.
- Si necesitas precisión, no olvides
(6).
7. Diseño de zonas horarias (almacenar en UTC, mostrar en hora local)
Al trabajar con la hora actual, un mal diseño de zonas horarias causa deriva temporal y conversiones dobles.
En la práctica, el enfoque predeterminado más seguro es “almacenar en UTC, convertir a hora local para mostrar.”

Figura: Arquitectura básica para almacenar UTC en MySQL y mostrar hora local
7.1 Verificar la zona horaria actual
Primero, verifica en qué zona horaria se ejecuta MySQL.
SELECT @@global.time_zone, @@session.time_zone;
@@global.time_zone→ configuración a nivel de servidor@@session.time_zone→ configuración de la conexión/sesión actual- Si muestra
SYSTEM, depende de la configuración del SO
Problemas comunes
- Los servidores de producción y desarrollo tienen zonas horarias de SO diferentes
- La aplicación convierte a UTC, y la BD convierte nuevamente (conversión doble)
7.2 Cambiar la zona horaria por sesión
SET time_zone = 'Asia/Tokyo';
o unificar a UTC:
SET time_zone = '+00:00';
Puntos importantes
- Revierte cuando se cierra la conexión
- Si usas agrupación de conexiones, verifica la configuración de tu aplicación
7.3 Por qué almacenar en UTC (principio del mundo real)
Política recomendada
- Almacena datos en UTC
- Convierte a la zona horaria del usuario al mostrar
Razones:
- Soporte internacional más fácil
- Evita problemas de horario de verano (DST)
- Minimiza el impacto al mover servidores
Obtén UTC:
SELECT UTC_TIMESTAMP();
7.4 Convertir zonas horarias con CONVERT_TZ()
Ejemplo: UTC → JST
SELECT CONVERT_TZ('2025-02-23 05:35:00', '+00:00', '+09:00');
Usando nombres de zonas horarias:
SELECT CONVERT_TZ('2025-02-23 05:35:00', 'UTC', 'Asia/Tokyo');
Ejemplo del mundo real
SELECT CONVERT_TZ(created_at, 'UTC', 'Asia/Tokyo')
FROM users;
7.5 Por qué CONVERT_TZ() devuelve NULL
Si devuelve NULL, las causas comunes incluyen:
- Las tablas de zonas horarias de MySQL no están cargadas
- El nombre de zona horaria especificado no existe
Ejemplo de carga en Linux:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
Notas
- En producción, confirma permisos y si se requiere un reinicio
- En Docker, zoneinfo puede no estar incluido dependiendo de la imagen
7.6 Cambiar la zona horaria a nivel de servidor
Archivo de configuración (my.cnf / my.ini):
[mysqld]
default_time_zone = '+00:00'
Verifica después del reinicio:
SELECT @@global.time_zone;
Importante
- Para cambios en producción, confirma el alcance del impacto
- Ten cuidado con datos existentes (a veces solo cambian las visualizaciones)
Errores comunes
- Almacenar hora local en lugar de UTC, y luego agregar soporte internacional
- Conversión doble entre la aplicación y la BD
- Diseñar con DATETIME sin considerar zonas horarias
- Configuraciones de TZ diferentes entre pruebas y producción
Lecciones clave de esta sección
- Almacena en UTC y convierte al mostrar.
- Usa
UTC_TIMESTAMP(). - Al usar
CONVERT_TZ(), verifica las tablas de TZ. - Siempre considera las diferencias de entorno en zonas horarias.
8. Ejemplos prácticos que puedes usar tal cual
Aquí hay ejemplos concretos de SQL que muestran cómo usar la hora actual de MySQL en desarrollo/operaciones reales.
Están escritos para que puedas copiar y pegar directamente.
8.1 Insertar automáticamente la hora actual en registros
Crear tabla
CREATE TABLE system_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
level VARCHAR(50),
message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Insertar registro
INSERT INTO system_logs (level, message)
VALUES ('ERROR', 'Failed to connect to the DB');
Puntos clave
created_atse inserta automáticamente- No es necesario pasar una marca de tiempo desde la aplicación
- Diseño esencial para auditoría e investigación de incidentes
Errores comunes
- Gestionar el tiempo tanto en la aplicación como en la base de datos
- Inconsistencias de tiempo debido a zonas horarias no unificadas
8.2 Actualizar la hora del último inicio de sesión
Diseño de tabla
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255),
last_login TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
);
Actualizar al iniciar sesión
UPDATE users
SET last_login = NOW()
WHERE id = 1;
Notas
ON UPDATEse dispara solo cuando la columna cambia- Algunas sentencias UPDATE pueden no cambiar el valor y, por lo tanto, no actualizar
8.3 Fijar una hora de referencia en trabajos por lotes
Para procesos de larga duración, es más seguro fijar una hora de referencia.
SET @base_time = NOW();
SELECT *
FROM orders
WHERE created_at >= @base_time - INTERVAL 1 DAY;
Por qué
- El tiempo no cambia a mitad del proceso
- Se mantiene la consistencia
8.4 Agregación para hoy / ayer / últimos 7 días
Ventas de hoy
SELECT SUM(amount)
FROM orders
WHERE created_at >= CURDATE()
AND created_at < CURDATE() + INTERVAL 1 DAY;
Ventas de ayer
SELECT SUM(amount)
FROM orders
WHERE created_at >= CURDATE() - INTERVAL 1 DAY
AND created_at < CURDATE();
Últimos 7 días
SELECT COUNT(*)
FROM users
WHERE created_at >= NOW() - INTERVAL 7 DAY;
Importante
- No aplique funciones a columnas en cláusulas WHERE
- Escriba condiciones que mantengan los índices utilizables
8.5 Verificaciones de expiración (sesiones/tokenes)
SELECT *
FROM sessions
WHERE expires_at > NOW();
Eliminar filas expiradas
DELETE FROM sessions
WHERE expires_at <= NOW();
Errores comunes
- Usar
CURDATE()e ignorar la hora - Almacenar en UTC pero comparar con
NOW()local
8.6 Obtener filas que expiran dentro de N horas
SELECT *
FROM coupons
WHERE expires_at <= NOW() + INTERVAL 1 HOUR;
Casos de uso:
- Alertas de expiración
- Notificaciones de vencimiento
Lo que siempre debes tener en cuenta
- Sea explícito si su referencia es “ahora” o “hoy 00:00”
- Escriba predicados que mantengan los índices efectivos
- Decida el diseño de zona horaria desde el principio
- No mezcle
NOW()yUTC_TIMESTAMP()sin una política clara
Puntos clave de esta sección
- Para registros/auditorías/seguimiento de actualizaciones, use
CURRENT_TIMESTAMP - Para agregación, use el patrón seguro
>= AND < - Para gestión de sesiones, compare con
NOW() - Fije una hora de referencia para mantener el procesamiento consistente
9. Preguntas Frecuentes (FAQ)
Aquí hay preguntas comunes sobre la hora actual en MySQL, especialmente los inconvenientes que aparecen en el trabajo real.
9.1 ¿Cuál es la diferencia entre NOW() y CURRENT_TIMESTAMP?
Conclusión: el significado del valor devuelto es el mismo.
SELECT NOW(), CURRENT_TIMESTAMP;
Ambos devuelven la fecha y hora actuales.
La principal diferencia es el uso sintáctico
CURRENT_TIMESTAMPpuede usarse enDEFAULTyON UPDATENOW()no puede usarse directamente en DEFAULT
Notas
- No es una diferencia de tipo
- Puede especificar precisión con argumentos como
(6)
9.2 ¿Debería usar SYSDATE()?
SYSDATE() devuelve la hora en el “momento de evaluación”.
SELECT SYSDATE(), SLEEP(2), SYSDATE();
El valor puede cambiar incluso dentro de la misma consulta.
Cuándo usarlo
- Cuando necesita registrar el instante de tiempo real exacto
Cuándo evitarlo
- Replicación
- Procesamiento de transacciones donde la consistencia es importante
En la mayoría de los casos, usar NOW() es suficiente.
9.3 ¿Por qué se desplaza la hora?
Causas principales:
- Configuraciones de zona horaria del servidor
- Configuraciones de zona horaria de la sesión
- Conversión doble con la aplicación
- Comportamiento de conversión automática del tipo TIMESTAMP
Verificar con:
SELECT @@global.time_zone, @@session.time_zone;
Mitigación
- Almacenar en UTC por defecto
- Convertir solo al mostrar
- Unificar la política entre la app y la DB
9.4 CONVERT_TZ() devuelve NULL
Causas:
- Las tablas de zona horaria no están cargadas
- Nombre de zona horaria incorrecto
Solución:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
Tener especial cuidado en entornos Docker.
9.5 Desplazamientos de rango con BETWEEN
Malo:
WHERE created_at BETWEEN '2025-02-01' AND '2025-02-10';
Seguro:
WHERE created_at >= '2025-02-01 00:00:00'
AND created_at < '2025-02-11 00:00:00';
Razones:
- Problema con el tiempo de límite final
- Fuga de microsegundos
- Eficiencia del índice
9.6 ¿Cómo elegir entre DATETIME y TIMESTAMP?
- Soporte internacional / gestión de UTC → TIMESTAMP
- Después de 2038 o datetimes fijos → DATETIME
Decidir esto durante el diseño.
9.7 No se están almacenando microsegundos
Causa:
- No se especificó precisión en la definición de la columna
Solución:
created_at DATETIME(6)
Lecciones clave de esta sección
- Comenzar con
NOW()yCURRENT_TIMESTAMP - Para consultas de rango, usar
>= AND < - El almacenamiento en UTC es el valor predeterminado más seguro
- No olvidar la selección de tipos y la precisión
10. Resumen
Este artículo organizó formas prácticas de recuperar, formatear, calcular y gestionar la hora actual en MySQL.
Finalmente, aquí están los mínimos esenciales que debes conocer para mantenerte seguro.
10.1 Obtener la hora actual (básicos)
- Recuperación general →
NOW() - DEFAULT de tabla / seguimiento de actualizaciones →
CURRENT_TIMESTAMP - Solo fecha →
CURDATE() - Solo hora →
CURTIME() - UTC →
UTC_TIMESTAMP() - Alta precisión →
NOW(6)
Reglas generales
- Si no estás seguro, usar
NOW()está bien en la mayoría de los casos. - Para el diseño de esquema, usar
CURRENT_TIMESTAMP.
10.2 Consultas de rango: “>= AND <” es la regla de oro
Patrón seguro:
WHERE created_at >= 'START'
AND created_at < 'END'
Por qué:
- Evita perder filas en el límite final
- Funciona con microsegundos
- Mantiene los índices utilizables
Ejemplo malo
WHERE DATE(created_at) = CURDATE();
No envuelvas la columna en una función.
10.3 Conceptos básicos de aritmética de datetime
- Sumar/restar →
INTERVAL - Diferencias de días →
DATEDIFF() - Diferencias de tiempo →
TIMESTAMPDIFF()
Siempre ten en mente si tu línea base es “ahora” o “hoy 00:00”.
10.4 Principios de diseño de zona horaria
- Almacenar en UTC
- Convertir al mostrar
- Unificar la política entre la app y la DB
Verificar con:
SELECT @@global.time_zone, @@session.time_zone;
10.5 Mejores prácticas de diseño de tabla
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
- Equipo estándar para auditoría/registro/seguimiento de actualizaciones
- Si necesitas precisión, especifica
(6)
Los 4 puntos más importantes del mundo real
- No malinterpretes la diferencia entre
NOW()yCURRENT_TIMESTAMP - Para consultas de rango, usar
>= AND < - Almacenar en UTC por defecto
- Decidir tipos (DATETIME vs TIMESTAMP) durante el diseño
El manejo de la hora actual en MySQL es fundamental para el registro, agregación de ventas, gestión de autenticación/sesiones, trabajos en lotes y más.
Si sigues los principios en este artículo, puedes evitar muchos problemas comunes como deriva de tiempo, errores en límites y degradación de rendimiento.


