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;| enero | febrero | abril | diciembre |
|---|---|---|---|
| 2025-01-31 | 2025-02-28 | 2025-04-30 | 2025-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_bisiesto | feb_normal |
|---|---|
| 2024-02-29 | 2025-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;| mes | cierre_mes | total_pedidos | ingresos |
|---|---|---|---|
| 2025-01 | 2025-01-31 | 342 | 89450.00 |
| 2025-02 | 2025-02-28 | 298 | 76230.50 |
| 2025-03 | 2025-03-31 | 365 | 94120.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;| hoy | fin_de_mes | dias_restantes |
|---|---|---|
| 2025-06-18 | 2025-06-30 | 12 |
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_febrero | inicio_marzo | inicio_enero_siguiente |
|---|---|---|
| 2025-02-01 | 2025-03-01 | 2026-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;| nombre | inicio_mes | fin_mes | total_ventas |
|---|---|---|---|
| Ana López | 2025-01-01 | 2025-01-31 | 15230.00 |
| Ana López | 2025-02-01 | 2025-02-28 | 12870.50 |
| Ana López | 2025-03-01 | 2025-03-31 | 16445.00 |
| Carlos Ruiz | 2025-01-01 | 2025-01-31 | 18900.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;| nulo | fecha_invalida |
|---|---|
| NULL | NULL |
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_mes | nombre_mes | dias_del_mes |
|---|---|---|
| 1 | January | 31 |
| 2 | February | 28 |
| 3 | March | 31 |
| 4 | April | 30 |
| 5 | May | 31 |
| 6 | June | 30 |
| 7 | July | 31 |
| 8 | August | 31 |
| 9 | September | 30 |
| 10 | October | 31 |
| 11 | November | 30 |
| 12 | December | 31 |
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
