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_diaultimo_diaultimo_dia_bisiesto
1365366

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;
nombrefecha_contrataciondia_del_ano
Ana Martinez2024-01-088
Maria Garcia2023-03-1574
Laura Fernandez2024-06-03155
Carlos Rodriguez2022-07-01182
Pedro Sanchez2023-11-20324

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);
anohasta_diatotal_pedidosingresos_acumulados
20254529873521.30
20264532782140.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_transcurridosdias_totalesporcentaje_completado
4536512.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_ytddias_con_datosproyeccion_anual
82140.5045666247.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_numerofecha
12025-01-01
3592025-12-25
3602025-12-26
3652025-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_nullfebrero_invalido
NULLNULL

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_diasdesdehastapedidosingresos
12025-01-022025-01-3014535800.20
22025-01-312025-03-0115237600.50
32025-03-022025-03-3116841200.80
42025-04-012025-04-3016540900.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