sqlpostgresqlmake-datemake-time

make_date y make_time en SQL: construir fechas y horas desde partes enteras

Como construir una fecha u hora desde columnas enteras separadas sin cadenas de formato y por que make_date valida partes fuera de rango.

3 min de lecturaReferencesql · postgresql · make-date · make-time · date-functions · mysql

make_date y make_time construyen un valor a partir de numeros ya listos: pasas el ano, el mes y el dia (o la hora, el minuto y el segundo) como enteros separados, sin analizar una cadena. Son la herramienta ideal cuando las partes de una fecha ya estan en columnas distintas de un informe.

Sintaxis basica

make_date(year, month, day) devuelve un date, y make_time(hour, min, sec) devuelve un time. Los argumentos son enteros (los segundos son double precision para guardar una fraccion). No hay cadenas de formato como 'YYYY-MM-DD' ni las sorpresas de configuracion regional que las acompanan.

SELECT
  make_date(2024, 3, 15)   AS d,    -- 2024-03-15
  make_time(14, 30, 0)     AS t;    -- 14:30:00

Los segundos aceptan una fraccion, asi que un momento con precision de milisegundos se construye sin convertir cadenas:

SELECT make_time(9, 5, 30.5) AS t;  -- 09:05:30.5

Una marca de tiempo completa la da make_timestamp(year, month, day, hour, min, sec), y la version con zona es make_timestamptz(...).

Construir una fecha desde columnas separadas

El escenario principal es una tabla donde el ano, el mes y el dia se guardan como numeros en campos distintos. Supongamos que una carga desde un origen heredado dejo las partes de la fecha por separado:

-- order_parts(order_id, y, m, d)
SELECT
  order_id,
  make_date(y, m, d) AS order_date
FROM order_parts;

make_date elimina el dolor de la concatenacion y el CAST: no tienes que construir la cadena y || '-' || m || '-' || d y confiar en que un mes de un solo digito no rompa el formato. Los numeros entran en la funcion tal cual.

El mismo truco es util para filtrar por una fecha construida desde parametros. Todos los pedidos de un dia concreto, armados desde tres numeros:

SELECT id, user_id, amount
FROM orders
WHERE created_at::date = make_date(2024, 3, 15)
  AND status = 'paid';

Validacion de partes fuera de rango

make_date no se queda callado ante un sinsentido: un valor fuera del rango valido lanza un error en lugar de pasar al mes siguiente como hace la aritmetica de intervalos.

SELECT make_date(2024, 13, 1);   -- ERROR: date field value out of range
SELECT make_date(2024, 2, 30);   -- ERROR: date field value out of range

Eso es a la vez una ventaja y una trampa:

  • Ventaja: los datos sucios (mes 13, dia 0) se detectan al instante en vez de convertirse en silencio en una fecha incorrecta.
  • Trampa: una sola fila rota en un recorrido grande tumba toda la consulta. Si los datos no estan limpios, valida las partes de antemano.

Protegete de las filas malas con un filtro previo:

SELECT order_id, make_date(y, m, d) AS order_date
FROM order_parts
WHERE m BETWEEN 1 AND 12
  AND d BETWEEN 1 AND 31;

Un ano negativo se lee como antes de Cristo, asi que make_date(-1, 1, 1) no es un error sino 0001-01-01 BC. El ano cero se rechaza.

Cuando conviene make_time

make_time brilla cuando la hora y el minuto vienen de una configuracion o de un join. Por ejemplo, normalizar la "ventana de trabajo" de un departamento a una unica hora de inicio:

SELECT
  e.dept,
  COUNT(*)                       AS people,
  make_time(9, 0, 0)             AS shift_start
FROM employees e
GROUP BY e.dept;

El time ya armado se puede sumar a una fecha para obtener un momento completo sin analizar cadenas:

SELECT
  u.id,
  make_date(2024, 3, 15) + make_time(18, 0, 0) AS reminder_at
FROM users u
WHERE u.country = 'DE';

Diferencias en MySQL y ClickHouse

Los nombres y el comportamiento difieren de PostgreSQL.

  • MySQL no tiene make_date en el sentido habitual. Tiene MAKEDATE(year, dayofyear), que espera un ano y un dia del ano, no un mes y un dia. Para construir desde mes y dia se usa STR_TO_DATE, y para la hora se usa MAKETIME(hour, minute, second).
SELECT
  STR_TO_DATE('2024-3-15', '%Y-%c-%e') AS d,   -- 2024-03-15
  MAKETIME(14, 30, 0)                  AS t;   -- 14:30:00
  • ClickHouse ofrece makeDate(year, month, day) y makeDateTime(year, month, day, hour, minute, second), cuyos nombres coinciden en sentido con PostgreSQL.
SELECT
  makeDate(2024, 3, 15)                  AS d,
  makeDateTime(2024, 3, 15, 14, 30, 0)   AS t;

La diferencia clave al portar: MySQL MAKEDATE toma un dia del ano, no un mes y un dia, y tragara sin quejarse una entrada que PostgreSQL rechazaria. Revisa siempre la firma antes de mover una consulta entre bases de datos.

Practica con ejercicios reales

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

Abrir el entrenador