HOUR

La función HOUR extrae el componente de hora de un valor TIME, DATETIME o TIMESTAMP y lo devuelve como un entero. Para valores DATETIME, el rango es de 0 a 23 (formato de 24 horas). Sin embargo, para valores TIME puros, el rango puede ser mucho mayor porque MySQL permite valores TIME que superen las 24 horas (hasta 838 horas), ya que TIME representa una duración, no solo un momento del día.

El análisis por hora es una herramienta poderosa para entender el comportamiento de usuarios y sistemas. ¿A qué hora compran más los clientes? ¿Cuándo se satura el servidor? ¿En qué franja horaria es más efectivo enviar emails? Todas estas preguntas se responden con HOUR y un buen GROUP BY.

Sintaxis

HOUR(expresion_tiempo)

El argumento puede ser un valor TIME, DATETIME, TIMESTAMP o una cadena con formato de hora válido. Devuelve un entero que representa la hora. Para DATETIME, el rango es 0-23. Para TIME, puede ser de 0 a 838.

Comportamiento básico

En su uso más común con valores DATETIME, HOUR devuelve la hora en formato de 24 horas:

SELECT
    HOUR('2025-08-15 09:30:00') AS maniana,
    HOUR('2025-08-15 14:45:00') AS tarde,
    HOUR('2025-08-15 23:59:59') AS noche,
    HOUR('2025-08-15 00:00:00') AS medianoche;
manianatardenochemedianoche
914230

La medianoche es la hora 0, el mediodía es la hora 12, y la última hora del día es la 23. No existe la hora 24 en un DATETIME.

HOUR con valores TIME

Con valores TIME puros, HOUR puede devolver valores mayores que 23 porque MySQL usa TIME tanto para momentos del día como para duraciones:

SELECT
    HOUR('10:30:00') AS hora_normal,
    HOUR('100:30:00') AS duracion_larga,
    HOUR('838:59:59') AS maximo_time;
hora_normalduracion_largamaximo_time
10100838

Esto es relevante si trabajas con columnas TIME que almacenan duraciones en lugar de horas del día. En la mayoría de aplicaciones web, trabajarás con DATETIME y el rango será siempre 0-23.

Caso práctico: análisis de tráfico por hora

El análisis horario más habitual es determinar en qué horas del día se concentra la actividad. Supongamos que tienes una tabla de accesos a tu tienda online:

SELECT
    HOUR(fecha_acceso) AS hora,
    COUNT(*) AS visitas,
    COUNT(DISTINCT id_usuario) AS usuarios_unicos
FROM log_accesos
WHERE DATE(fecha_acceso) = '2025-01-15'
GROUP BY HOUR(fecha_acceso)
ORDER BY hora;
horavisitasusuarios_unicos
04532
12218
21512
68967
7234178
8567423
9890645
101023756
11978698
12745534
13812589
14934678
15876623
16723512
17654467
18534389
19423312
20356267
21278198
22189134
2311278

Este perfil horario muestra dos picos claros: uno por la mañana entre las 9 y las 11, y otro por la tarde entre las 14 y las 15. La información entre las 3 y las 5 de la madrugada es tan baja que ni aparece en los resultados. Estos datos son fundamentales para decidir cuándo lanzar campañas o cuándo programar el mantenimiento del servidor.

Caso práctico: detectar horas pico de ventas

Para una tienda online, saber las horas pico de ventas permite dimensionar la infraestructura y el equipo de soporte:

SELECT
    HOUR(fecha_venta) AS hora,
    COUNT(*) AS ventas,
    SUM(total) AS ingresos,
    ROUND(AVG(total), 2) AS ticket_medio
FROM ventas
WHERE YEAR(fecha_venta) = 2024
    AND MONTH(fecha_venta) = 12
GROUP BY HOUR(fecha_venta)
ORDER BY ingresos DESC
LIMIT 5;
horaventasingresosticket_medio
108715660.00180.00
148214760.00180.00
117813260.00170.00
207313140.00180.00
96811560.00170.00

Las 10 de la mañana son la hora con más ingresos, seguidas de las 14h y las 11h. Dato interesante: las 20h (8 de la noche) aparecen en el top 5, lo que sugiere que muchos clientes compran por la noche desde casa después del trabajo.

Caso práctico: filtrar por horario laboral

Muchos análisis necesitan separar las operaciones dentro del horario laboral de las que ocurren fuera de él. HOUR hace esto trivial:

SELECT
    CASE
        WHEN HOUR(fecha_registro) BETWEEN 9 AND 17 THEN 'Horario laboral (9-18h)'
        WHEN HOUR(fecha_registro) BETWEEN 18 AND 22 THEN 'Tarde-noche (18-23h)'
        ELSE 'Madrugada (23-9h)'
    END AS franja,
    COUNT(*) AS registros,
    ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER(), 1) AS porcentaje
FROM pedidos
WHERE YEAR(fecha_registro) = 2024
GROUP BY
    CASE
        WHEN HOUR(fecha_registro) BETWEEN 9 AND 17 THEN 'Horario laboral (9-18h)'
        WHEN HOUR(fecha_registro) BETWEEN 18 AND 22 THEN 'Tarde-noche (18-23h)'
        ELSE 'Madrugada (23-9h)'
    END
ORDER BY registros DESC;
franjaregistrosporcentaje
Horario laboral (9-18h)325062.5
Tarde-noche (18-23h)146028.1
Madrugada (23-9h)4909.4

Este desglose confirma que la mayoría de pedidos se realizan en horario laboral, pero casi el 28% llega por la tarde-noche. Si tu servicio de atención al cliente cierra a las 18h, estás dejando sin soporte a casi un tercio de tus compradores.

Caso práctico: rendimiento del equipo por turno

Si tu empresa trabaja por turnos, HOUR te permite evaluar la productividad de cada turno:

SELECT
    CASE
        WHEN HOUR(fecha_cierre) BETWEEN 6 AND 13 THEN 'Turno mañana'
        WHEN HOUR(fecha_cierre) BETWEEN 14 AND 21 THEN 'Turno tarde'
        ELSE 'Turno noche'
    END AS turno,
    COUNT(*) AS tickets_cerrados,
    ROUND(AVG(TIMESTAMPDIFF(MINUTE, fecha_apertura, fecha_cierre)), 0) AS tiempo_resolucion_min
FROM tickets_soporte
WHERE YEAR(fecha_cierre) = 2024
    AND estado = 'cerrado'
GROUP BY
    CASE
        WHEN HOUR(fecha_cierre) BETWEEN 6 AND 13 THEN 'Turno mañana'
        WHEN HOUR(fecha_cierre) BETWEEN 14 AND 21 THEN 'Turno tarde'
        ELSE 'Turno noche'
    END;
turnotickets_cerradostiempo_resolucion_min
Turno mañana452035
Turno tarde389042
Turno noche124058

El turno de mañana resuelve más tickets y en menos tiempo. El turno de noche es más lento, lo cual tiene sentido: menor volumen pero también menor personal disponible para escalar problemas.

HOUR con NULL

HOUR devuelve NULL si la entrada es NULL:

SELECT
    HOUR(NULL) AS resultado;
resultado
NULL

Si tu tabla tiene columnas DATETIME que admiten nulos, las filas con valor NULL no se incluirán en las agrupaciones por hora, lo cual es generalmente el comportamiento deseado.

Combinación con otras funciones

HOUR se combina con MINUTE para crear franjas horarias más precisas, y con DATE para análisis cruzados de día y hora:

SELECT
    DATE(fecha_venta) AS dia,
    HOUR(fecha_venta) AS hora,
    COUNT(*) AS ventas
FROM ventas
WHERE fecha_venta >= '2024-12-23'
    AND fecha_venta < '2024-12-26'
GROUP BY DATE(fecha_venta), HOUR(fecha_venta)
HAVING COUNT(*) > 10
ORDER BY dia, hora;
diahoraventas
2024-12-231023
2024-12-231119
2024-12-231421
2024-12-24931
2024-12-241045
2024-12-241138
2024-12-241227

Este análisis combinado día-hora muestra cómo el 24 de diciembre concentra un volumen de ventas muy superior al 23, especialmente en la franja de 9 a 12 de la mañana, justo antes de las compras de última hora navideñas.

En el siguiente artículo veremos MINUTE para extraer los minutos.

Escrito por Eduardo Lázaro