DAYOFYEAR
La funcion DAYOFYEAR() devuelve un entero que representa la posicion de un dia dentro del ano, contando desde el 1 de enero. El 1 de enero es el dia 1, el 2 de enero es el dia 2, y asi sucesivamente hasta el 31 de diciembre que sera el dia 365 (o 366 en anos bisiestos). Este concepto se conoce tambien como "dia juliano" y resulta muy util para calcular intervalos, comparar el mismo periodo entre distintos anos y medir el progreso a lo largo del ano.
Sintaxis
DAYOFYEAR(fecha)El parametro fecha acepta un valor de tipo DATE o DATETIME. La funcion devuelve un entero entre 1 y 366. Si la fecha es NULL o no es valida, devuelve NULL.
Comportamiento basico
Para obtener el dia del ano de una fecha concreta:
SELECT DAYOFYEAR('2026-02-14') AS dia_del_ano;| dia_del_ano |
|---|
| 45 |
El 14 de febrero es el dia 45 del ano (31 dias de enero mas 14 de febrero). Puedes verificar los extremos del ano:
SELECT
DAYOFYEAR('2026-01-01') AS primer_dia,
DAYOFYEAR('2026-12-31') AS ultimo_dia,
DAYOFYEAR('2024-12-31') AS ultimo_dia_bisiesto;| primer_dia | ultimo_dia | ultimo_dia_bisiesto |
|---|---|---|
| 1 | 365 | 366 |
El ano 2024 fue bisiesto, por lo que su ultimo dia es el 366. El 2026 no es bisiesto, asi que termina en 365. Esta diferencia es relevante cuando haces comparaciones entre anos.
Puedes aplicar DAYOFYEAR() sobre columnas de tus tablas:
SELECT
nombre,
fecha_contratacion,
DAYOFYEAR(fecha_contratacion) AS dia_del_ano
FROM empleados
ORDER BY DAYOFYEAR(fecha_contratacion)
LIMIT 5;| nombre | fecha_contratacion | dia_del_ano |
|---|---|---|
| Ana Martinez | 2024-01-08 | 8 |
| Maria Garcia | 2023-03-15 | 74 |
| Laura Fernandez | 2024-06-03 | 155 |
| Carlos Rodriguez | 2022-07-01 | 182 |
| Pedro Sanchez | 2023-11-20 | 324 |
Caso practico: comparar el mismo periodo entre anos
Una de las aplicaciones mas poderosas de DAYOFYEAR() es comparar datos acumulados hasta el mismo punto del ano en periodos diferentes. Por ejemplo, si quieres comparar las ventas acumuladas hasta el 14 de febrero de 2025 y 2026:
SELECT
YEAR(fecha_pedido) AS ano,
MAX(DAYOFYEAR(fecha_pedido)) AS hasta_dia,
COUNT(*) AS total_pedidos,
ROUND(SUM(total), 2) AS ingresos_acumulados
FROM pedidos
WHERE DAYOFYEAR(fecha_pedido) <= DAYOFYEAR('2026-02-14')
AND YEAR(fecha_pedido) IN (2025, 2026)
GROUP BY YEAR(fecha_pedido);| ano | hasta_dia | total_pedidos | ingresos_acumulados |
|---|---|---|---|
| 2025 | 45 | 298 | 73521.30 |
| 2026 | 45 | 327 | 82140.50 |
Esta consulta compara los primeros 45 dias de cada ano, independientemente de que el calendario caiga de forma diferente. Es una forma mas precisa de comparar periodos que simplemente filtrar por mes, porque garantiza exactamente el mismo numero de dias.
Caso practico: progreso del ano y proyecciones
DAYOFYEAR() te permite calcular que porcentaje del ano ha transcurrido y proyectar resultados anuales a partir de los datos acumulados:
SELECT
DAYOFYEAR(CURDATE()) AS dias_transcurridos,
CASE
WHEN YEAR(CURDATE()) % 4 = 0
AND (YEAR(CURDATE()) % 100 != 0 OR YEAR(CURDATE()) % 400 = 0)
THEN 366
ELSE 365
END AS dias_totales,
ROUND(DAYOFYEAR(CURDATE()) * 100.0 /
CASE
WHEN YEAR(CURDATE()) % 4 = 0
AND (YEAR(CURDATE()) % 100 != 0 OR YEAR(CURDATE()) % 400 = 0)
THEN 366
ELSE 365
END, 1) AS porcentaje_completado;| dias_transcurridos | dias_totales | porcentaje_completado |
|---|---|---|
| 45 | 365 | 12.3 |
Puedes usar este porcentaje para proyectar ventas anuales basandote en los datos hasta la fecha:
SELECT
ROUND(SUM(total), 2) AS ingresos_ytd,
DAYOFYEAR(MAX(fecha_pedido)) AS dias_con_datos,
ROUND(SUM(total) / DAYOFYEAR(MAX(fecha_pedido)) * 365, 2) AS proyeccion_anual
FROM pedidos
WHERE YEAR(fecha_pedido) = 2026;| ingresos_ytd | dias_con_datos | proyeccion_anual |
|---|---|---|
| 82140.50 | 45 | 666247.39 |
La proyeccion anual se calcula dividiendo los ingresos acumulados entre los dias transcurridos y multiplicando por 365. Es una estimacion lineal que asume un ritmo de ventas constante, lo que puede no ser preciso si tu negocio tiene estacionalidad marcada, pero sirve como referencia rapida.
Caso practico: detectar periodos de inactividad
DAYOFYEAR() facilita la deteccion de periodos sin actividad. Si quieres encontrar dias del ano en los que no hubo ninguna venta:
SELECT
d.dia_numero,
DATE_ADD('2025-01-01', INTERVAL d.dia_numero - 1 DAY) AS fecha
FROM (
SELECT a.N + b.N * 10 + c.N * 100 + 1 AS dia_numero
FROM (SELECT 0 AS 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) a
CROSS JOIN (SELECT 0 AS 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) b
CROSS JOIN (SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) c
) d
WHERE d.dia_numero <= 365
AND d.dia_numero NOT IN (
SELECT DAYOFYEAR(fecha_pedido)
FROM pedidos
WHERE YEAR(fecha_pedido) = 2025
)
ORDER BY d.dia_numero
LIMIT 10;| dia_numero | fecha |
|---|---|
| 1 | 2025-01-01 |
| 359 | 2025-12-25 |
| 360 | 2025-12-26 |
| 365 | 2025-12-31 |
El resultado muestra que en 2025 no hubo pedidos el 1 de enero ni durante las fiestas navidenas, lo cual es logico para muchos comercios.
Manejo de NULL
DAYOFYEAR() devuelve NULL cuando recibe un valor nulo o una fecha invalida:
SELECT
DAYOFYEAR(NULL) AS resultado_null,
DAYOFYEAR('2025-02-29') AS febrero_invalido;| resultado_null | febrero_invalido |
|---|---|
| NULL | NULL |
El 29 de febrero de 2025 no existe (2025 no es bisiesto), por lo que MySQL devuelve NULL. Esto puede pasar inadvertido si no validas las fechas de entrada, asi que conviene tenerlo en cuenta en tus filtros:
SELECT
nombre,
fecha_contratacion,
DAYOFYEAR(fecha_contratacion) AS dia_ano
FROM empleados
WHERE fecha_contratacion IS NOT NULL;Combinacion con otras funciones
DAYOFYEAR() se combina especialmente bien con funciones aritmeticas para calcular diferencias en dias dentro del mismo ano:
SELECT
nombre,
fecha_contratacion,
DAYOFYEAR(fecha_contratacion) AS dia_inicio,
DAYOFYEAR(CURDATE()) AS dia_actual,
CASE
WHEN YEAR(fecha_contratacion) = YEAR(CURDATE())
THEN DAYOFYEAR(CURDATE()) - DAYOFYEAR(fecha_contratacion)
ELSE NULL
END AS dias_trabajados_este_ano
FROM empleados
WHERE YEAR(fecha_contratacion) = YEAR(CURDATE())
ORDER BY fecha_contratacion;Tambien puedes usarla con MOD() para agrupar datos en periodos personalizados, como bloques de 30 dias:
SELECT
FLOOR((DAYOFYEAR(fecha_pedido) - 1) / 30) + 1 AS periodo_30_dias,
MIN(fecha_pedido) AS desde,
MAX(fecha_pedido) AS hasta,
COUNT(*) AS pedidos,
ROUND(SUM(total), 2) AS ingresos
FROM pedidos
WHERE YEAR(fecha_pedido) = 2025
GROUP BY FLOOR((DAYOFYEAR(fecha_pedido) - 1) / 30) + 1
ORDER BY periodo_30_dias;| periodo_30_dias | desde | hasta | pedidos | ingresos |
|---|---|---|---|---|
| 1 | 2025-01-02 | 2025-01-30 | 145 | 35800.20 |
| 2 | 2025-01-31 | 2025-03-01 | 152 | 37600.50 |
| 3 | 2025-03-02 | 2025-03-31 | 168 | 41200.80 |
| 4 | 2025-04-01 | 2025-04-30 | 165 | 40900.00 |
Este enfoque divide el ano en bloques de exactamente 30 dias, lo que a veces es mas util que agrupar por meses calendario, que tienen duraciones variables.
En el siguiente articulo veremos WEEK para obtener el numero de semana.
Escrito por Eduardo Lázaro
