TIMESTAMPDIFF

La funcion TIMESTAMPDIFF calcula la diferencia entre dos valores datetime y la expresa en la unidad que tú elijas: segundos, minutos, horas, días, semanas, meses, trimestres o años. Es la función más versátil de MySQL para medir intervalos de tiempo y la respuesta correcta siempre que necesites saber "cuántos meses han pasado", "cuántos años tiene" o "cuántas horas transcurrieron" entre dos fechas.

Sintaxis

TIMESTAMPDIFF(unidad, datetime1, datetime2)

El primer argumento es la unidad en la que deseas el resultado. Las unidades válidas son SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER y YEAR. Los dos argumentos siguientes son valores DATE o DATETIME. El resultado es datetime2 - datetime1 expresado como un entero en la unidad especificada.

Observa que el orden de los argumentos es diferente al de DATEDIFF y TIMEDIFF: aquí la fecha más antigua va primero y la más reciente después. Si datetime2 es anterior a datetime1, el resultado será negativo.

Comportamiento básico

Calcular la diferencia en días entre dos fechas:

SELECT TIMESTAMPDIFF(DAY, '2026-01-01', '2026-03-15') AS dias;
dias
73

Para obtener la misma diferencia en semanas, meses o años, solo cambias la unidad:

SELECT
    TIMESTAMPDIFF(DAY, '2024-06-15', '2026-02-14') AS dias,
    TIMESTAMPDIFF(WEEK, '2024-06-15', '2026-02-14') AS semanas,
    TIMESTAMPDIFF(MONTH, '2024-06-15', '2026-02-14') AS meses,
    TIMESTAMPDIFF(YEAR, '2024-06-15', '2026-02-14') AS anios;
diassemanasmesesanios
61087191

TIMESTAMPDIFF trunca el resultado al entero inferior. En el ejemplo, aunque han pasado 19 meses y algunos días, la unidad MONTH devuelve 19, no 20. Del mismo modo, YEAR devuelve 1 porque aún no se han completado 2 años enteros.

La función también trabaja con precisión horaria cuando usas unidades de tiempo:

SELECT
    TIMESTAMPDIFF(HOUR, '2026-03-15 09:00:00', '2026-03-16 14:30:00') AS horas,
    TIMESTAMPDIFF(MINUTE, '2026-03-15 09:00:00', '2026-03-15 11:45:00') AS minutos,
    TIMESTAMPDIFF(SECOND, '2026-03-15 09:00:00', '2026-03-15 09:05:30') AS segundos;
horasminutossegundos
29165330

Las horas se truncan a 29 (no 29.5) porque TIMESTAMPDIFF siempre devuelve un entero.

Caso práctico: calcular la edad de una persona

Calcular la edad en años es el caso de uso emblemático de TIMESTAMPDIFF. Es la forma más fiable de hacerlo en MySQL porque maneja correctamente los años bisiestos y las variaciones en la cantidad de días por mes:

SELECT
    nombre,
    fecha_nacimiento,
    TIMESTAMPDIFF(YEAR, fecha_nacimiento, CURDATE()) AS edad
FROM empleados
ORDER BY edad DESC
LIMIT 5;
nombrefecha_nacimientoedad
Roberto Díaz1968-05-2257
Elena Ruiz1975-11-0850
Javier Moreno1980-03-1545
María García1985-09-3040
Carlos López1990-07-1235

Este cálculo es correcto porque TIMESTAMPDIFF(YEAR, ...) solo cuenta un año cuando ha transcurrido completo. Si una persona nació el 15 de marzo de 1990, el 14 de marzo de 2026 aún tendrá 35 años, y el 15 de marzo cumplirá 36.

Para mayor detalle, puedes mostrar la edad desglosada:

SELECT
    nombre,
    fecha_nacimiento,
    TIMESTAMPDIFF(YEAR, fecha_nacimiento, CURDATE()) AS anios,
    TIMESTAMPDIFF(MONTH, fecha_nacimiento, CURDATE()) % 12 AS meses_adicionales
FROM empleados
WHERE id = 1;
nombrefecha_nacimientoaniosmeses_adicionales
María García1985-09-30404

El operador módulo % 12 extrae los meses restantes después de los años completos, dando como resultado "40 años y 4 meses".

Caso práctico: antigüedad laboral y beneficios

En sistemas de recursos humanos, la antigüedad determina beneficios como días de vacaciones o bonificaciones:

SELECT
    e.nombre,
    e.fecha_contratacion,
    TIMESTAMPDIFF(YEAR, e.fecha_contratacion, CURDATE()) AS anios_antiguedad,
    TIMESTAMPDIFF(MONTH, e.fecha_contratacion, CURDATE()) AS meses_totales,
    CASE
        WHEN TIMESTAMPDIFF(YEAR, e.fecha_contratacion, CURDATE()) >= 10 THEN 30
        WHEN TIMESTAMPDIFF(YEAR, e.fecha_contratacion, CURDATE()) >= 5 THEN 25
        WHEN TIMESTAMPDIFF(YEAR, e.fecha_contratacion, CURDATE()) >= 2 THEN 20
        ELSE 15
    END AS dias_vacaciones
FROM empleados e
WHERE e.activo = 1
ORDER BY anios_antiguedad DESC
LIMIT 5;
nombrefecha_contratacionanios_antiguedadmeses_totalesdias_vacaciones
Roberto Díaz2010-03-011519130
Elena Ruiz2015-08-151012630
Javier Moreno2018-01-1089725
María García2020-06-0156825
Carlos López2023-09-1522820

La combinación de TIMESTAMPDIFF con CASE permite asignar beneficios graduales basados en la antigüedad, un patrón muy común en aplicaciones de RRHH.

Caso práctico: métricas de tiempo por etapa

En un sistema de gestión de proyectos o pipeline de ventas, medir cuánto tiempo permanece un registro en cada etapa permite identificar cuellos de botella:

SELECT
    o.id AS oportunidad_id,
    o.cliente,
    o.etapa,
    o.fecha_cambio_etapa,
    TIMESTAMPDIFF(DAY, o.fecha_cambio_etapa, CURDATE()) AS dias_en_etapa,
    TIMESTAMPDIFF(HOUR, o.fecha_cambio_etapa, NOW()) AS horas_en_etapa
FROM oportunidades_venta o
WHERE o.etapa != 'cerrada'
ORDER BY dias_en_etapa DESC
LIMIT 5;
oportunidad_idclienteetapafecha_cambio_etapadias_en_etapahoras_en_etapa
78Empresa ABCnegociacion2026-01-05 10:00:0040965
112Corp XYZpropuesta2026-01-20 14:30:0025601
145Servicios JKLcontacto2026-02-01 09:00:0013319
189Tech MNOdemo2026-02-07 16:00:007167
201Grupo PQRcontacto2026-02-10 11:15:004100

Una oportunidad que lleva 40 días en la etapa de negociación podría requerir intervención. La granularidad en horas (horas_en_etapa) añade precisión cuando los días no son suficientes.

Para un resumen agregado por etapa:

SELECT
    etapa,
    COUNT(*) AS total,
    ROUND(AVG(TIMESTAMPDIFF(DAY, fecha_cambio_etapa, CURDATE())), 1) AS promedio_dias,
    MAX(TIMESTAMPDIFF(DAY, fecha_cambio_etapa, CURDATE())) AS maximo_dias
FROM oportunidades_venta
WHERE etapa != 'cerrada'
GROUP BY etapa
ORDER BY promedio_dias DESC;
etapatotalpromedio_diasmaximo_dias
negociacion828.540
propuesta1218.325
demo159.215
contacto235.813

Manejo de NULL

Si cualquiera de los argumentos datetime es NULL, el resultado es NULL:

SELECT
    TIMESTAMPDIFF(DAY, NULL, '2026-03-15') AS primero_nulo,
    TIMESTAMPDIFF(DAY, '2026-03-15', NULL) AS segundo_nulo;
primero_nulosegundo_nulo
NULLNULL

En la práctica, las columnas de fecha que aún no tienen valor (como fecha_finalizacion de un proyecto en curso) son NULL. Usa COALESCE o IFNULL para sustituirlas:

SELECT
    nombre_proyecto,
    TIMESTAMPDIFF(DAY, fecha_inicio,
        COALESCE(fecha_finalizacion, CURDATE())
    ) AS dias_transcurridos
FROM proyectos;

Esto calcula los días desde el inicio hasta la finalización, o hasta hoy si el proyecto aún no ha terminado.

Combinación con otras funciones

TIMESTAMPDIFF se integra naturalmente con funciones de agregación para generar métricas estadísticas:

SELECT
    ROUND(AVG(TIMESTAMPDIFF(HOUR, fecha_creacion, fecha_cierre)), 1) AS promedio_horas,
    MIN(TIMESTAMPDIFF(HOUR, fecha_creacion, fecha_cierre)) AS minimo_horas,
    MAX(TIMESTAMPDIFF(HOUR, fecha_creacion, fecha_cierre)) AS maximo_horas
FROM tickets_soporte
WHERE fecha_cierre IS NOT NULL
  AND fecha_creacion >= DATE_SUB(CURDATE(), INTERVAL 30 DAY);
promedio_horasminimo_horasmaximo_horas
18.5196

También puedes usarla dentro de expresiones condicionales para crear segmentaciones:

SELECT
    CASE
        WHEN TIMESTAMPDIFF(MONTH, fecha_registro, CURDATE()) < 3 THEN 'Nuevo'
        WHEN TIMESTAMPDIFF(MONTH, fecha_registro, CURDATE()) < 12 THEN 'Regular'
        ELSE 'Veterano'
    END AS segmento,
    COUNT(*) AS total_clientes
FROM clientes
GROUP BY segmento;
segmentototal_clientes
Nuevo145
Regular312
Veterano543

Esta segmentación por antigüedad es fundamental para análisis de marketing y retención de clientes.

En el siguiente artículo veremos TIMESTAMPADD para sumar intervalos en unidades específicas.

Escrito por Eduardo Lázaro