Cuando sumas y restas intervalos en PostgreSQL, el resultado suele quedar "crudo": 36 horas, 90 dias, 800 minutos. La familia JUSTIFY_* reordena ese intervalo en algo legible, pasando las horas sobrantes a dias y los dias sobrantes a meses.
Tres funciones y que arrastra cada una
PostgreSQL incluye tres funciones de normalizacion, cada una responsable de un "lugar":
JUSTIFY_HOURS(i) toma cada 24 horas y las convierte en 1 dia.
JUSTIFY_DAYS(i) toma cada 30 dias y los convierte en 1 mes.
JUSTIFY_INTERVAL(i) hace ambos acarreos a la vez y concilia los signos.
SELECT JUSTIFY_HOURS(INTERVAL '36 hours') AS h,
JUSTIFY_DAYS(INTERVAL '90 days') AS d,
JUSTIFY_INTERVAL(INTERVAL '1 mon 33 days 27 hours') AS full;
Fijate en que ninguna aplana el intervalo a segundos. PostgreSQL guarda un intervalo en tres campos independientes — meses, dias y microsegundos — y JUSTIFY_* solo mueve valores entre esos campos sin cambiar el significado.
Para que sirve: duracion de un pedido
Supongamos que medimos cuanto lleva un pedido en proceso. La resta simple de tiempos da un intervalo donde todo se acumula en horas:
SELECT id,
created_at,
NOW() - created_at AS raw_age
FROM orders
WHERE status = 'processing';
La cadena 52:30:00 es tecnicamente correcta pero incomoda en un informe. Envuelve el resultado en JUSTIFY_HOURS:
SELECT id,
JUSTIFY_HOURS(NOW() - created_at) AS readable_age
FROM orders
WHERE status = 'processing';
Ahora se lee como "2 dias y algunas horas" — justo lo que un responsable espera ver.
Antiguedad de empleados y grandes cantidades de dias
JUSTIFY_DAYS es muy util cuando acumulas dias y quieres una estimacion aproximada en meses. Imagina agregar dias por departamento:
SELECT dept,
JUSTIFY_DAYS(SUM(NOW() - created_at)) AS dept_tenure
FROM employees
GROUP BY dept;
Cuando un intervalo lleva horas y dias al mismo tiempo, recurre a JUSTIFY_INTERVAL — ordena en todos los frentes. Una buena costumbre es normalizar una diferencia antes de compararla, para que los signos mixtos no te jueguen una mala pasada:
SELECT name,
JUSTIFY_INTERVAL(salary_review_at - hired_at) AS service
FROM employees
ORDER BY service DESC;
Cuidado: 30 dias no es un mes de calendario
La trampa principal de JUSTIFY_DAYS y JUSTIFY_INTERVAL es que tratan un mes como exactamente 30 dias. Los meses reales nunca son asi: febrero tiene 28 o 29 dias, julio tiene 31.
SELECT JUSTIFY_INTERVAL(INTERVAL '60 days');
Por eso un intervalo normalizado sirve para informes y etiquetas legibles, pero es erroneo para aritmetica exacta de fechas. Ten clara la distincion:
JUSTIFY_* aplica reglas fijas: 24 horas = 1 dia, 30 dias = 1 mes.
AGE(end, start) mira el calendario real y devuelve meses y dias verdaderos.
Si necesitas una "edad" honesta, usa AGE. Si solo quieres mostrar una duracion acumulada con orden, usa JUSTIFY_*.
Diferencias en otros motores
Las funciones JUSTIFY_* son propias de PostgreSQL; no hay equivalente directo en otros motores.
- MySQL: no existe un tipo
interval de primera clase, asi que las diferencias suelen vivir como segundos o dias. Normalizas a mano con DIV y MOD, por ejemplo seconds DIV 86400 para dias.
- ClickHouse: los intervalos tampoco se normalizan solos; usa
dateDiff('day', ...) y aritmetica numerica simple para derivar los lugares que quieras.
En resumen: JUSTIFY_* es una comodidad de presentacion que vive en PostgreSQL. En codigo portable, normaliza las duraciones tu mismo y recuerda que "un mes igual a 30 dias" es una simplificacion, no una verdad del calendario.
Cuando sumas y restas intervalos en PostgreSQL, el resultado suele quedar "crudo": 36 horas, 90 dias, 800 minutos. La familia
JUSTIFY_*reordena ese intervalo en algo legible, pasando las horas sobrantes a dias y los dias sobrantes a meses.Tres funciones y que arrastra cada una
PostgreSQL incluye tres funciones de normalizacion, cada una responsable de un "lugar":
JUSTIFY_HOURS(i)toma cada 24 horas y las convierte en 1 dia.JUSTIFY_DAYS(i)toma cada 30 dias y los convierte en 1 mes.JUSTIFY_INTERVAL(i)hace ambos acarreos a la vez y concilia los signos.SELECT JUSTIFY_HOURS(INTERVAL '36 hours') AS h, -- 1 day 12:00:00 JUSTIFY_DAYS(INTERVAL '90 days') AS d, -- 3 mons JUSTIFY_INTERVAL(INTERVAL '1 mon 33 days 27 hours') AS full; -- full -> 2 mons 4 days 03:00:00Fijate en que ninguna aplana el intervalo a segundos. PostgreSQL guarda un intervalo en tres campos independientes — meses, dias y microsegundos — y
JUSTIFY_*solo mueve valores entre esos campos sin cambiar el significado.Para que sirve: duracion de un pedido
Supongamos que medimos cuanto lleva un pedido en proceso. La resta simple de tiempos da un intervalo donde todo se acumula en horas:
SELECT id, created_at, NOW() - created_at AS raw_age FROM orders WHERE status = 'processing'; -- raw_age podria ser, por ejemplo, '52:30:00'La cadena
52:30:00es tecnicamente correcta pero incomoda en un informe. Envuelve el resultado enJUSTIFY_HOURS:SELECT id, JUSTIFY_HOURS(NOW() - created_at) AS readable_age FROM orders WHERE status = 'processing'; -- readable_age -> 2 days 04:30:00Ahora se lee como "2 dias y algunas horas" — justo lo que un responsable espera ver.
Antiguedad de empleados y grandes cantidades de dias
JUSTIFY_DAYSes muy util cuando acumulas dias y quieres una estimacion aproximada en meses. Imagina agregar dias por departamento:SELECT dept, JUSTIFY_DAYS(SUM(NOW() - created_at)) AS dept_tenure FROM employees GROUP BY dept;Cuando un intervalo lleva horas y dias al mismo tiempo, recurre a
JUSTIFY_INTERVAL— ordena en todos los frentes. Una buena costumbre es normalizar una diferencia antes de compararla, para que los signos mixtos no te jueguen una mala pasada:SELECT name, JUSTIFY_INTERVAL(salary_review_at - hired_at) AS service FROM employees ORDER BY service DESC;Cuidado: 30 dias no es un mes de calendario
La trampa principal de
JUSTIFY_DAYSyJUSTIFY_INTERVALes que tratan un mes como exactamente 30 dias. Los meses reales nunca son asi: febrero tiene 28 o 29 dias, julio tiene 31.SELECT JUSTIFY_INTERVAL(INTERVAL '60 days'); -- 2 mons (no "1 mes mas algunos dias de calendario")Por eso un intervalo normalizado sirve para informes y etiquetas legibles, pero es erroneo para aritmetica exacta de fechas. Ten clara la distincion:
JUSTIFY_*aplica reglas fijas: 24 horas = 1 dia, 30 dias = 1 mes.AGE(end, start)mira el calendario real y devuelve meses y dias verdaderos.Si necesitas una "edad" honesta, usa
AGE. Si solo quieres mostrar una duracion acumulada con orden, usaJUSTIFY_*.Diferencias en otros motores
Las funciones
JUSTIFY_*son propias de PostgreSQL; no hay equivalente directo en otros motores.intervalde primera clase, asi que las diferencias suelen vivir como segundos o dias. Normalizas a mano conDIVyMOD, por ejemploseconds DIV 86400para dias.dateDiff('day', ...)y aritmetica numerica simple para derivar los lugares que quieras.En resumen:
JUSTIFY_*es una comodidad de presentacion que vive en PostgreSQL. En codigo portable, normaliza las duraciones tu mismo y recuerda que "un mes igual a 30 dias" es una simplificacion, no una verdad del calendario.