MAKETIME
La función MAKETIME construye un valor de tipo TIME a partir de tres componentes individuales: horas, minutos y segundos. Es el equivalente temporal de lo que MAKEDATE() hace con fechas: en lugar de manipular cadenas de texto con formatos propensos a errores, puedes ensamblar la hora desde sus partes numéricas de forma segura y legible.
En la práctica, MAKETIME aparece en muchos escenarios del mundo real. El más frecuente es cuando tus datos almacenan las horas, minutos y segundos en columnas separadas, algo habitual en importaciones de hojas de cálculo, bases de datos legacy o integraciones con sistemas externos que no siguen el formato estándar HH:MM:SS. Otro escenario común es cuando necesitas construir horarios dinámicamente a partir de cálculos aritméticos, por ejemplo al generar franjas horarias para un sistema de citas o al redondear tiempos a intervalos fijos para facturación.
La función es determinista, lo que significa que siempre produce el mismo resultado para los mismos argumentos de entrada. Esto la hace segura para usar en vistas, columnas generadas y expresiones que MySQL necesita evaluar de forma predecible.
Sintaxis
La sintaxis de MAKETIME es directa, ya que recibe exactamente tres argumentos numéricos que representan cada componente del tiempo.
MAKETIME(hora, minuto, segundo)Los tres argumentos son valores enteros (o expresiones que se evalúan a enteros). La función devuelve un valor de tipo TIME en formato HH:MM:SS. El rango válido para las horas va de -838 a 838, que es el rango completo del tipo TIME en MySQL. Los minutos deben estar entre 0 y 59, y los segundos también entre 0 y 59. Si los minutos o los segundos caen fuera de ese rango, MySQL devuelve NULL en lugar de intentar corregir el valor.
Esta asimetría en la validación (horas flexibles, minutos y segundos estrictos) responde al diseño del tipo TIME en MySQL, que no solo sirve para representar horas del reloj (0 a 23), sino también para almacenar intervalos de tiempo que pueden superar las 24 horas o incluso ser negativos. Por eso las horas pueden exceder ampliamente el rango de un día.
Comportamiento básico
Construir valores de hora desde sus componentes con MAKETIME es directo e intuitivo. Cada argumento numérico se coloca en su posición correspondiente dentro del formato HH:MM:SS.
SELECT
MAKETIME(9, 30, 0) AS manana,
MAKETIME(14, 15, 45) AS tarde,
MAKETIME(23, 59, 59) AS casi_medianoche,
MAKETIME(0, 0, 0) AS medianoche;| manana | tarde | casi_medianoche | medianoche |
|---|---|---|---|
| 09:30:00 | 14:15:45 | 23:59:59 | 00:00:00 |
Es importante recordar que MySQL usa el formato de 24 horas, por lo que las 2:15 PM se representan como 14:15:00 y no existe el concepto de AM/PM en el tipo TIME. Si necesitas convertir de formato 12 horas a 24 horas, puedes hacerlo fácilmente con aritmética antes de llamar a MAKETIME: si la hora es PM y no es las 12, simplemente sumas 12 al componente de hora.
Valores fuera de rango
El comportamiento de MAKETIME con valores fuera de rango es uno de sus aspectos más importantes y a veces confusos. MySQL es estricto con los minutos y segundos, pero sorprendentemente flexible con las horas.
SELECT
MAKETIME(100, 0, 0) AS cien_horas,
MAKETIME(-5, 30, 0) AS negativa,
MAKETIME(10, 60, 0) AS minutos_invalidos,
MAKETIME(10, 30, 60) AS segundos_invalidos;| cien_horas | negativa | minutos_invalidos | segundos_invalidos |
|---|---|---|---|
| 100:00:00 | -05:30:00 | NULL | NULL |
Las horas pueden exceder las 24 porque el tipo TIME de MySQL se diseñó para representar no solo horas del reloj, sino también duraciones o intervalos de tiempo. Un valor como 100:00:00 es perfectamente válido y representa un intervalo de cien horas. Las horas negativas también son válidas y representan un intervalo negativo, algo útil al calcular diferencias de tiempo que resultan en un valor anterior al punto de referencia.
Sin embargo, 60 minutos o 60 segundos producen NULL porque no son valores válidos dentro de la estructura de un tiempo. MySQL no realiza "carry" automático, es decir, no convierte 60 segundos en 1 minuto y 0 segundos. Si necesitas ese comportamiento, debes normalizar los valores tú mismo antes de llamar a MAKETIME, o bien usar SEC_TO_TIME que convierte un número total de segundos en un valor TIME con la normalización ya aplicada.
Precisión de segundos fraccionarios
Aunque MAKETIME acepta formalmente valores enteros, si pasas un número con decimales, MySQL truncará la parte fraccionaria del componente de segundos. Si tu aplicación necesita microsegundos, deberás construir el valor de tiempo mediante cadenas y CAST en lugar de usar MAKETIME.
SELECT
MAKETIME(10, 30, 45) AS entero,
MAKETIME(10, 30, 45.789) AS con_decimales;| entero | con_decimales |
|---|---|
| 10:30:45 | 10:30:45.789000 |
En versiones recientes de MySQL, los segundos fraccionarios se preservan, lo que puede ser útil para aplicaciones que requieren precisión temporal más allá del segundo.
Caso práctico: unificar columnas de tiempo separadas
Uno de los escenarios más frecuentes para MAKETIME es consolidar datos temporales que llegan fragmentados en columnas independientes. Esto ocurre constantemente cuando importas datos desde hojas de cálculo de Excel o Google Sheets, donde los usuarios suelen escribir la hora, los minutos y los segundos en celdas separadas en lugar de usar un formato temporal unificado.
Imagina una tabla de registros de entrada de empleados importada desde una hoja de cálculo de recursos humanos.
CREATE TABLE registros_entrada (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100),
fecha DATE,
hora_entrada INT,
minuto_entrada INT,
segundo_entrada INT
);
INSERT INTO registros_entrada (nombre, fecha, hora_entrada, minuto_entrada, segundo_entrada) VALUES
('Ana López', '2025-06-15', 8, 45, 12),
('Carlos Ruiz', '2025-06-15', 9, 2, 33),
('María García', '2025-06-15', 8, 30, 0),
('Pedro Sánchez', '2025-06-15', 9, 15, 48),
('Laura Martín', '2025-06-15', 8, 58, 5);Con MAKETIME puedes combinar las tres columnas en un único valor temporal que MySQL puede comparar, ordenar y usar en cálculos de diferencia de tiempo.
SELECT
nombre,
hora_entrada,
minuto_entrada,
segundo_entrada,
MAKETIME(hora_entrada, minuto_entrada, segundo_entrada) AS hora_completa
FROM registros_entrada;| nombre | hora_entrada | minuto_entrada | segundo_entrada | hora_completa |
|---|---|---|---|---|
| Ana López | 8 | 45 | 12 | 08:45:12 |
| Carlos Ruiz | 9 | 2 | 33 | 09:02:33 |
| María García | 8 | 30 | 0 | 08:30:00 |
| Pedro Sánchez | 9 | 15 | 48 | 09:15:48 |
| Laura Martín | 8 | 58 | 5 | 08:58:05 |
Una vez que tienes el valor TIME unificado, puedes realizar operaciones que serían imposibles o muy engorrosas con las columnas separadas. Por ejemplo, encontrar quién llegó antes de las 9:00 de la mañana se convierte en una comparación directa.
SELECT nombre, MAKETIME(hora_entrada, minuto_entrada, segundo_entrada) AS hora_completa
FROM registros_entrada
WHERE MAKETIME(hora_entrada, minuto_entrada, segundo_entrada) < MAKETIME(9, 0, 0);| nombre | hora_completa |
|---|---|
| Ana López | 08:45:12 |
| María García | 08:30:00 |
| Laura Martín | 08:58:05 |
Sin MAKETIME, tendrías que escribir una condición compuesta comparando primero las horas, luego los minutos y finalmente los segundos, lo que sería mucho más verbose y propenso a errores lógicos.
Caso práctico: construir horarios de turnos
Cuando gestionas turnos de trabajo, a menudo necesitas generar los horarios de inicio y fin a partir de parámetros numéricos almacenados en una tabla de configuración. MAKETIME facilita esta tarea al permitir que los horarios se definan simplemente como números enteros de horas, que son más fáciles de almacenar y manipular que cadenas de texto formateadas.
SELECT
turno,
MAKETIME(hora_inicio, 0, 0) AS inicio,
MAKETIME(hora_fin, 0, 0) AS fin,
TIMEDIFF(MAKETIME(hora_fin, 0, 0), MAKETIME(hora_inicio, 0, 0)) AS duracion
FROM (
SELECT 'Manana' AS turno, 6 AS hora_inicio, 14 AS hora_fin
UNION SELECT 'Tarde', 14, 22
UNION SELECT 'Noche', 22, 6
) turnos;| turno | inicio | fin | duracion |
|---|---|---|---|
| Manana | 06:00:00 | 14:00:00 | 08:00:00 |
| Tarde | 14:00:00 | 22:00:00 | 08:00:00 |
| Noche | 22:00:00 | 06:00:00 | -16:00:00 |
Observa que el turno de noche produce una duración negativa porque la hora de fin (6:00) es menor que la de inicio (22:00). En estos casos necesitas lógica adicional para sumar 24 horas a la diferencia. Una forma de resolverlo es usar una expresión condicional que detecte los turnos que cruzan la medianoche.
SELECT
turno,
MAKETIME(hora_inicio, 0, 0) AS inicio,
MAKETIME(hora_fin, 0, 0) AS fin,
CASE
WHEN hora_fin > hora_inicio
THEN TIMEDIFF(MAKETIME(hora_fin, 0, 0), MAKETIME(hora_inicio, 0, 0))
ELSE ADDTIME(
TIMEDIFF(MAKETIME(24, 0, 0), MAKETIME(hora_inicio, 0, 0)),
MAKETIME(hora_fin, 0, 0)
)
END AS duracion
FROM (
SELECT 'Manana' AS turno, 6 AS hora_inicio, 14 AS hora_fin
UNION SELECT 'Tarde', 14, 22
UNION SELECT 'Noche', 22, 6
) turnos;| turno | inicio | fin | duracion |
|---|---|---|---|
| Manana | 06:00:00 | 14:00:00 | 08:00:00 |
| Tarde | 14:00:00 | 22:00:00 | 08:00:00 |
| Noche | 22:00:00 | 06:00:00 | 08:00:00 |
Ahora todos los turnos muestran correctamente su duración de 8 horas. La clave es que MAKETIME permite construir los valores temporales necesarios para los cálculos intermedios de forma clara y legible. La función TIMEDIFF() se encarga de calcular la diferencia entre los tiempos construidos.
Caso práctico: combinar fecha y hora construida
Un uso potente de MAKETIME es combinarlo con una fecha para crear un valor DATETIME completo. Esto es habitual cuando almacenas la fecha de una cita y la hora de la cita en columnas separadas (la fecha como DATE y la hora como componentes numéricos) y necesitas un valor temporal unificado para comparaciones, ordenamientos o cálculos de diferencia.
SELECT
e.nombre,
e.fecha_cita,
MAKETIME(e.hora_cita, e.minuto_cita, 0) AS hora_construida,
CAST(CONCAT(e.fecha_cita, ' ', MAKETIME(e.hora_cita, e.minuto_cita, 0)) AS DATETIME) AS cita_completa
FROM (
SELECT 'Ana López' AS nombre, '2025-06-20' AS fecha_cita, 10 AS hora_cita, 30 AS minuto_cita
UNION SELECT 'Carlos Ruiz', '2025-06-20', 14, 0
UNION SELECT 'María García', '2025-06-21', 9, 15
) e;| nombre | fecha_cita | hora_construida | cita_completa |
|---|---|---|---|
| Ana López | 2025-06-20 | 10:30:00 | 2025-06-20 10:30:00 |
| Carlos Ruiz | 2025-06-20 | 14:00:00 | 2025-06-20 14:00:00 |
| María García | 2025-06-21 | 09:15:00 | 2025-06-21 09:15:00 |
La combinación de CONCAT con CAST a DATETIME te permite crear un valor temporal completo que puedes usar para operaciones como calcular cuánto falta para cada cita, ordenar las citas cronológicamente, o filtrar las que caen dentro de un rango específico. Este patrón es la forma estándar de unir componentes de fecha y hora cuando vienen de fuentes de datos diferentes.
Caso práctico: generar franjas horarias para un sistema de citas
Un escenario muy práctico es generar dinámicamente las franjas horarias disponibles para un sistema de reservas o citas. Si tu consultorio médico atiende en intervalos de 30 minutos desde las 9:00 hasta las 17:00, puedes generar todas las franjas usando MAKETIME combinado con una tabla de números auxiliar.
SELECT
MAKETIME(9 + FLOOR(n / 2), (n % 2) * 30, 0) AS franja_inicio,
MAKETIME(9 + FLOOR((n + 1) / 2), ((n + 1) % 2) * 30, 0) AS franja_fin
FROM (
SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3
UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 UNION SELECT 11
UNION SELECT 12 UNION SELECT 13 UNION SELECT 14 UNION SELECT 15
) numeros
WHERE MAKETIME(9 + FLOOR(n / 2), (n % 2) * 30, 0) < MAKETIME(17, 0, 0);Esta consulta genera todas las franjas de 30 minutos entre las 9:00 y las 17:00. La aritmética con FLOOR y el operador módulo calcula la hora y los minutos correspondientes a cada franja, y MAKETIME convierte esos números en valores temporales que MySQL puede manejar nativamente.
MAKETIME con NULL
Si cualquiera de los tres argumentos es NULL, la función devuelve NULL. Este comportamiento es consistente con la regla general de MySQL de que cualquier operación aritmética o de función que involucre un NULL produce NULL como resultado.
SELECT
MAKETIME(NULL, 30, 0) AS hora_nula,
MAKETIME(10, NULL, 0) AS minuto_nulo,
MAKETIME(10, 30, NULL) AS segundo_nulo;| hora_nula | minuto_nulo | segundo_nulo |
|---|---|---|
| NULL | NULL | NULL |
Este comportamiento significa que debes validar tus datos de entrada antes de usar MAKETIME si existe la posibilidad de valores nulos en alguno de los componentes. Una forma de protegerte es usar COALESCE para proporcionar valores por defecto cuando algún componente pueda ser nulo.
SELECT MAKETIME(
COALESCE(hora_col, 0),
COALESCE(minuto_col, 0),
COALESCE(segundo_col, 0)
) AS hora_segura
FROM mi_tabla;De esta forma, si alguno de los componentes es NULL, se usa 0 como valor por defecto en lugar de propagar el NULL al resultado final.
Combinación con otras funciones de fecha y hora
MAKETIME se combina naturalmente con las funciones de extracción HOUR(), MINUTE() y SECOND() para descomponer y reconstruir valores de tiempo con transformaciones intermedias. Este patrón de "extraer, modificar, reconstruir" es extremadamente útil para manipulaciones temporales que no tienen una función dedicada en MySQL.
Un ejemplo clásico es redondear una hora al cuarto de hora más cercano, algo que se necesita con frecuencia en sistemas de facturación por horas, donde no se factura por minuto sino por bloques de 15 minutos.
SELECT
hora_original,
MAKETIME(
HOUR(hora_original),
FLOOR(MINUTE(hora_original) / 15) * 15,
0
) AS hora_redondeada
FROM (
SELECT '09:07:33' AS hora_original
UNION SELECT '14:22:10'
UNION SELECT '16:48:55'
UNION SELECT '20:59:01'
) horas;| hora_original | hora_redondeada |
|---|---|
| 09:07:33 | 09:00:00 |
| 14:22:10 | 14:15:00 |
| 16:48:55 | 16:45:00 |
| 20:59:01 | 20:45:00 |
La lógica toma la hora original, mantiene las horas intactas, redondea los minutos al múltiplo de 15 inferior usando FLOOR y la división entera, y descarta los segundos estableciéndolos a cero. El resultado es un tiempo "limpio" alineado a cuartos de hora.
Otro uso habitual es añadir o restar un offset a un componente específico del tiempo. Por ejemplo, para crear una hora que sea exactamente 2 horas después de otra.
SELECT
hora_original,
MAKETIME(HOUR(hora_original) + 2, MINUTE(hora_original), SECOND(hora_original)) AS dos_horas_despues
FROM (
SELECT MAKETIME(10, 30, 0) AS hora_original
UNION SELECT MAKETIME(22, 45, 0)
) horas;| hora_original | dos_horas_despues |
|---|---|
| 10:30:00 | 12:30:00 |
| 22:45:00 | 24:45:00 |
Observa que 24:45:00 es un valor TIME válido en MySQL, aunque exceda las 24 horas del reloj. Si necesitas que el resultado se mantenga dentro del rango de un día, deberás aplicar módulo 24 al componente de horas.
Errores comunes
El error más frecuente al usar MAKETIME es pasar minutos o segundos iguales o superiores a 60, esperando que MySQL realice la conversión automática. MySQL no hace carry: MAKETIME(10, 75, 0) devuelve NULL, no 11:15:00. Si tus datos pueden contener valores fuera de rango, normalízalos primero o usa SEC_TO_TIME que sí maneja la conversión.
-- Esto devuelve NULL, no 11:15:00
SELECT MAKETIME(10, 75, 0) AS resultado;| resultado |
|---|
| NULL |
La forma correcta de manejar minutos que exceden 59 es convertir todo a segundos, sumar, y luego usar SEC_TO_TIME.
SELECT SEC_TO_TIME(10 * 3600 + 75 * 60 + 0) AS resultado;| resultado |
|---|
| 11:15:00 |
Otro error común es asumir que MAKETIME solo acepta horas entre 0 y 23. Como se explicó antes, el tipo TIME de MySQL puede representar intervalos, por lo que valores como MAKETIME(150, 30, 0) son perfectamente válidos y producen 150:30:00.
Cuándo usar MAKETIME
Usa MAKETIME siempre que necesites construir un valor de tipo TIME a partir de componentes numéricos separados. Los escenarios más habituales incluyen la unificación de datos importados donde las horas, minutos y segundos vienen en columnas independientes, la generación dinámica de horarios y franjas de tiempo a partir de parámetros de configuración, el redondeo o normalización de tiempos mediante la descomposición y reconstrucción con transformaciones intermedias, y la combinación con fechas para crear valores DATETIME completos.
Si tus datos ya están en formato HH:MM:SS como cadena de texto, no necesitas MAKETIME; MySQL convierte automáticamente las cadenas al tipo TIME cuando el formato es correcto. MAKETIME brilla cuando los componentes están separados o cuando necesitas construir el tiempo desde cálculos aritméticos. También puedes usar MAKETIME junto con SEC_TO_TIME cuando necesitas un control más explícito sobre la construcción del valor temporal, especialmente en consultas que otros desarrolladores necesitarán leer y mantener.
En el siguiente artículo veremos FROM_DAYS para convertir un número de días en una fecha.
Escrito por Eduardo Lázaro
