LAST_DAY

La función LAST_DAY recibe una fecha y devuelve el último día del mes correspondiente. Parece una operación sencilla, pero es sorprendentemente útil porque la cantidad de días varía de un mes a otro: enero tiene 31, febrero tiene 28 o 29, abril tiene 30... En lugar de memorizar estas reglas o escribir lógica condicional, LAST_DAY se encarga de todo, incluyendo el cálculo correcto de los años bisiestos.

En la práctica, LAST_DAY es una herramienta fundamental para generar reportes mensuales, calcular vencimientos, determinar cuántos días faltan para terminar el mes y construir rangos de fechas que abarquen meses completos.

Sintaxis

LAST_DAY(fecha)

La función acepta cualquier expresión que MySQL pueda interpretar como una fecha o datetime. Devuelve siempre un valor de tipo DATE (sin componente de hora), correspondiente al último día del mes de la fecha proporcionada.

Comportamiento básico

El uso más directo de LAST_DAY es averiguar cuál es el último día de un mes determinado. Veamos cómo se comporta con distintos meses:

SELECT
    LAST_DAY('2025-01-15') AS enero,
    LAST_DAY('2025-02-10') AS febrero,
    LAST_DAY('2025-04-22') AS abril,
    LAST_DAY('2025-12-01') AS diciembre;
enerofebreroabrildiciembre
2025-01-312025-02-282025-04-302025-12-31

Observa cómo LAST_DAY sabe que enero tiene 31 días, febrero tiene 28 en 2025, abril tiene 30 y diciembre tiene 31. No importa qué día del mes le pases; siempre devuelve el último.

Años bisiestos

LAST_DAY maneja correctamente los años bisiestos. Febrero de 2024 tiene 29 días, mientras que febrero de 2025 tiene 28:

SELECT
    LAST_DAY('2024-02-01') AS feb_bisiesto,
    LAST_DAY('2025-02-01') AS feb_normal;
feb_bisiestofeb_normal
2024-02-292025-02-28

Esta es precisamente la razón por la que LAST_DAY es tan valiosa: codificar manualmente la lógica de años bisiestos es propenso a errores, y la función lo resuelve de forma nativa.

Caso práctico: reportes de fin de mes

Uno de los usos más comunes de LAST_DAY es filtrar datos que corresponden al cierre de un mes. Imagina que tienes una tabla de ventas y quieres calcular el total vendido en cada mes, asegurándote de incluir todas las transacciones hasta el último día:

SELECT
    DATE_FORMAT(fecha, '%Y-%m') AS mes,
    LAST_DAY(fecha) AS cierre_mes,
    COUNT(*) AS total_pedidos,
    SUM(total) AS ingresos
FROM pedidos
WHERE fecha BETWEEN '2025-01-01' AND LAST_DAY('2025-03-01')
GROUP BY DATE_FORMAT(fecha, '%Y-%m'), LAST_DAY(fecha)
ORDER BY mes;
mescierre_mestotal_pedidosingresos
2025-012025-01-3134289450.00
2025-022025-02-2829876230.50
2025-032025-03-3136594120.75

El uso de LAST_DAY('2025-03-01') en el WHERE garantiza que el rango incluye hasta el 31 de marzo, sin tener que recordar cuántos días tiene ese mes.

Caso práctico: calcular días restantes del mes

Si quieres saber cuántos días faltan para terminar el mes actual, puedes combinar LAST_DAY con CURDATE y DATEDIFF:

SELECT
    CURDATE() AS hoy,
    LAST_DAY(CURDATE()) AS fin_de_mes,
    DATEDIFF(LAST_DAY(CURDATE()), CURDATE()) AS dias_restantes;
hoyfin_de_mesdias_restantes
2025-06-182025-06-3012

Este cálculo es útil en dashboards de gestión donde necesitas mostrar cuánto tiempo queda para alcanzar objetivos mensuales, o para planificar la distribución de tareas en lo que resta del período.

Caso práctico: obtener el primer día del mes siguiente

Un truco muy extendido es sumar un día a LAST_DAY para obtener el primer día del mes siguiente. Esto es especialmente útil para construir rangos de fechas sin solapamientos:

SELECT
    LAST_DAY('2025-01-15') + INTERVAL 1 DAY AS inicio_febrero,
    LAST_DAY('2025-02-10') + INTERVAL 1 DAY AS inicio_marzo,
    LAST_DAY('2025-12-25') + INTERVAL 1 DAY AS inicio_enero_siguiente;
inicio_febreroinicio_marzoinicio_enero_siguiente
2025-02-012025-03-012026-01-01

Con este patrón puedes construir intervalos mensuales exactos. Por ejemplo, para generar facturas que cubran el mes completo de cada empleado:

SELECT
    e.nombre,
    DATE_FORMAT(v.fecha, '%Y-%m-01') AS inicio_mes,
    LAST_DAY(v.fecha) AS fin_mes,
    SUM(v.monto) AS total_ventas
FROM empleados e
JOIN ventas v ON e.id = v.empleado_id
WHERE v.fecha >= '2025-01-01' AND v.fecha < LAST_DAY('2025-03-01') + INTERVAL 1 DAY
GROUP BY e.nombre, DATE_FORMAT(v.fecha, '%Y-%m-01'), LAST_DAY(v.fecha)
ORDER BY e.nombre, inicio_mes;
nombreinicio_mesfin_mestotal_ventas
Ana López2025-01-012025-01-3115230.00
Ana López2025-02-012025-02-2812870.50
Ana López2025-03-012025-03-3116445.00
Carlos Ruiz2025-01-012025-01-3118900.75

Observa que el WHERE usa < LAST_DAY(...) + INTERVAL 1 DAY en lugar de <= LAST_DAY(...). Esta técnica es más segura cuando la columna fecha incluye componente de hora, ya que <= '2025-03-31' excluiría registros del 31 de marzo con hora distinta de medianoche.

Caso práctico: calcular el primer día del mes actual

Otra derivación útil es obtener el primer día del mes de una fecha dada. Aunque no existe una función FIRST_DAY, puedes calcularla combinando LAST_DAY con aritmética de fechas:

SELECT
    LAST_DAY('2025-06-18' - INTERVAL 1 MONTH) + INTERVAL 1 DAY AS primer_dia;
primer_dia
2025-06-01

La lógica es: retrocede un mes (mayo 18), calcula su último día (mayo 31) y súmale un día (junio 1). Este patrón te permite construir rangos completos del mes corriente sin hardcodear el día 1.

LAST_DAY con NULL

Como la mayoría de funciones en MySQL, LAST_DAY devuelve NULL cuando recibe un argumento nulo o una fecha inválida:

SELECT
    LAST_DAY(NULL) AS nulo,
    LAST_DAY('2025-02-30') AS fecha_invalida;
nulofecha_invalida
NULLNULL

Febrero no tiene día 30, así que MySQL considera la entrada inválida y devuelve NULL. Es importante validar tus datos de entrada si sospechas que pueden contener fechas mal formadas.

Combinación con otras funciones

LAST_DAY se combina frecuentemente con funciones de formato y aritmética de fechas. Un ejemplo habitual es calcular cuántos días tiene cada mes de un año:

SELECT
    MONTH(fecha) AS num_mes,
    MONTHNAME(fecha) AS nombre_mes,
    DAY(LAST_DAY(fecha)) AS dias_del_mes
FROM (
    SELECT DATE_ADD('2025-01-01', INTERVAL n MONTH) AS fecha
    FROM (SELECT 0 n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3
          UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
          UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 UNION SELECT 11) meses
) calendario;
num_mesnombre_mesdias_del_mes
1January31
2February28
3March31
4April30
5May31
6June30
7July31
8August31
9September30
10October31
11November30
12December31

El truco está en DAY(LAST_DAY(fecha)): primero obtenemos el último día del mes, y luego extraemos el número de día, que nos dice exactamente cuántos días tiene ese mes.

En el siguiente artículo veremos MAKEDATE para construir una fecha a partir de año y día del año.

Escrito por Eduardo Lázaro