sqlpostgresqldatesinterval

AGE en SQL: diferencias de fechas como intervalos y edad en anos

La funcion AGE de PostgreSQL devuelve la diferencia entre fechas como anos, meses y dias, no como dias sueltos.

2 min de lecturaReferencesql · postgresql · dates · interval · age

En PostgreSQL la funcion AGE responde a la pregunta "cuanto tiempo ha pasado" como lo entiende una persona: en anos, meses y dias, no como un conteo crudo de dias. Por eso es la herramienta natural para la edad de usuarios, la antiguedad de empleados o lo viejo que es un pedido.

Dos argumentos frente a uno

AGE tiene dos formas. Con dos argumentos calcula el intervalo entre dos momentos, y el orden es AGE(end_ts, start_ts) — fin menos inicio.

SELECT AGE(TIMESTAMP '2024-03-01', TIMESTAMP '2021-11-15') AS gap;
-- 2 years 3 mons 14 days

Con un solo argumento el punto de referencia pasa a ser current_date de forma implicita (medianoche de hoy), asi que el resultado cambia dia a dia:

SELECT AGE(TIMESTAMP '1990-06-17') AS since_birth;
-- evaluated against midnight today

Comparalo con la resta simple. El operador - sobre dos fechas devuelve solo un numero de dias — util, pero nunca responde "cuantos anos".

SELECT DATE '2024-03-01' - DATE '2021-11-15' AS raw_days;  -- 837

Edad de usuarios y antiguedad de pedidos

Tomemos nuestras tablas. Para saber cuanto tiempo lleva un usuario con su cuenta, pasa created_at como argumento unico:

SELECT id, email, AGE(created_at) AS account_age
FROM users
ORDER BY created_at;

La antiguedad de un pedido funciona igual. Es comodo filtrar por el intervalo directamente — PostgreSQL compara un interval contra un literal:

SELECT o.id, o.amount, AGE(o.created_at) AS order_age
FROM orders o
WHERE o.status = 'paid'
  AND AGE(o.created_at) > INTERVAL '90 days';

Para empleados AGE describe la antiguedad con claridad cuando tienes una fecha de alta (aqui created_at hace de punto de inicio):

SELECT name, dept, AGE(NOW(), created_at) AS tenure
FROM employees
ORDER BY tenure DESC;

Por que los meses dependen del calendario

El rasgo clave del interval que devuelve AGE: los meses se cuentan por el calendario, no como 30 dias fijos. PostgreSQL avanza la fecha primero por anos completos, luego por meses completos, y solo el resto se expresa en dias.

SELECT AGE(DATE '2024-03-31', DATE '2024-01-31') AS feb_gap;
-- 2 mons  (not 60 days, not 59)

Por eso el mismo lapso en dias puede dar un numero de meses distinto — febrero es mas corto que julio. Es correcto para una "edad", pero fuente de sorpresas si esperabas aritmetica en dias.

  • AGE siempre normaliza el resultado en anos, meses y dias.
  • Los meses de calendario no son 30 dias — la duracion depende de las fechas reales.
  • Para un conteo exacto de dias usa la resta date - date o EXTRACT(EPOCH ...).

Extraer anos con EXTRACT

El intervalo se imprime bien, pero para comparar y agrupar necesitas un numero. EXTRACT saca un campo del intervalo:

SELECT id, email,
       EXTRACT(YEAR FROM AGE(created_at))::int AS full_years
FROM users;

Cuidado: EXTRACT(YEAR FROM AGE(...)) devuelve solo los anos del intervalo y descarta los meses. Para un usuario con "2 anos 11 meses" obtienes 2, no un 3 redondeado. Si quieres anos cumplidos es justo lo correcto; si quieres el total de meses, calcula years * 12 + months.

SELECT id,
       EXTRACT(YEAR  FROM AGE(created_at)) * 12
     + EXTRACT(MONTH FROM AGE(created_at)) AS total_months
FROM users;

Diferencias en otros motores

AGE es una extension de PostgreSQL sin equivalente directo en el estandar.

  • MySQL: usa TIMESTAMPDIFF(YEAR, start, end) para anos completos o DATEDIFF para dias. No hay un intervalo anos-meses-dias listo para usar.
  • ClickHouse: ofrece age('year', start, end) y dateDiff('day', ...); siempre indicas la unidad de forma explicita, y tampoco hay un intervalo simbolico.

Si el codigo debe ser portable, calcula los anos con TIMESTAMPDIFF/dateDiff, y construye el intervalo legible solo en PostgreSQL.

Practica con ejercicios reales

Resuelve ejercicios en el entrenador de SQL con corrección instantánea y pistas.

Abrir el entrenador