📅

Funciones de Fecha y Hora

DATE, DATEDIFF, EOMONTH, NETWORKDAYS y todo lo que necesitas para trabajar con fechas en DAX

Principiante Intermedio
🔍

Cómo trata DAX las fechas internamente 🤔

Analogía kawaii 🌸
En DAX, las fechas son en realidad números enteros disfrazados de fechas. El 1 de enero de 1900 es el día 1, el 2 de enero de 1900 es el 2... y así sucesivamente. Esto significa que puedes hacer aritmética con fechas: restar dos fechas te da el número de días entre ellas.

Tipos importantes:

  • DateTime: fecha + hora (número decimal — la parte entera es la fecha, la decimal la hora)
  • Date: solo fecha (entero)
  • Time: solo hora (decimal entre 0 y 1)
⚠️ Cuidado con el locale
Las fechas en DAX son sensibles al locale. Si tu modelo usa fechas en formato USA (MM/DD/YYYY) y tus datos vienen en formato europeo (DD/MM/YYYY), puedes tener problemas silenciosos. Siempre usa la función DATE() o DATEVALUE() para construir fechas de forma explícita.
🏗️

Construir fechas y horas

DATE(<año>, <mes>, <día>)
Principiante
Crea un valor de fecha a partir de año, mes y día. La forma más segura de construir fechas en DAX, sin ambigüedad de locale.

Ejemplos:

// Crear fecha fija
Inicio Año Actual = DATE(YEAR(TODAY()), 1, 1)
// Equivale al 1 de enero del año actual

// Crear fecha hace 6 meses exactos
Hace 6 Meses = DATE(YEAR(TODAY()), MONTH(TODAY()) - 6, DAY(TODAY()))
// DAX gestiona automáticamente el "desbordamiento" de meses
// Si hoy es marzo, MONTH() - 6 = -3 → DAX lo convierte a septiembre del año anterior

// Útil para límites dinámicos en filtros:
Inicio Trimestre Actual =
DATE(YEAR(TODAY()), (ROUNDUP(MONTH(TODAY())/3, 0) - 1) * 3 + 1, 1)
TIME(<hora>, <minuto>, <segundo>)
Principiante
Crea un valor de hora. Devuelve un decimal entre 0 (00:00:00) y 0.999... (23:59:59).

Ejemplo:

Turno Mañana Inicio = TIME(8, 0, 0)   -- 08:00:00
Turno Tarde Inicio = TIME(14, 30, 0)  -- 14:30:00
DATEVALUE(<texto>) | TIMEVALUE(<texto>)
Principiante
Convierte texto en fecha/hora. Útil cuando los datos vienen como strings y necesitas tratarlos como fechas.

Ejemplo:

// Si una columna tiene fechas como texto "2024-01-15":
FechaConvertida = DATEVALUE("2024-01-15")

// ¡Cuidado con el formato! Depende del locale del modelo:
DATEVALUE("15/01/2024")  -- formato europeo
DATEVALUE("01/15/2024")  -- formato USA
🔍

Extraer partes de una fecha

Tabla de referencia completa:

FUNCIÓN DEVUELVE RANGO EJEMPLO
YEAR(fecha) Año completo 1900-9999 YEAR(TODAY()) → 2024
MONTH(fecha) Número de mes 1-12 MONTH(TODAY()) → 3
DAY(fecha) Día del mes 1-31 DAY(TODAY()) → 15
HOUR(fecha) Hora 0-23 HOUR(NOW()) → 14
MINUTE(fecha) Minuto 0-59 MINUTE(NOW()) → 30
SECOND(fecha) Segundo 0-59 SECOND(NOW()) → 45
WEEKDAY(fecha, [tipo]) Día de la semana 1-7 WEEKDAY(TODAY(), 2) → 1 (lunes=1)
WEEKNUM(fecha, [tipo]) Semana del año 1-53 WEEKNUM(TODAY(), 21) → 15 (ISO)
QUARTER(fecha) Trimestre 1-4 QUARTER(TODAY()) → 1

Ejemplos prácticos en columnas calculadas:

// En tabla Calendario — columnas calculadas útiles:
'Calendario'[Año] = YEAR('Calendario'[Fecha])
'Calendario'[Mes Num] = MONTH('Calendario'[Fecha])
'Calendario'[Mes Nombre] = FORMAT('Calendario'[Fecha], "MMMM")
'Calendario'[Trimestre] = "Q" & QUARTER('Calendario'[Fecha])
'Calendario'[Semana ISO] = WEEKNUM('Calendario'[Fecha], 21)
'Calendario'[Es Fin Semana] = IF(WEEKDAY('Calendario'[Fecha], 2) >= 6, TRUE, FALSE)
'Calendario'[Año-Mes] = FORMAT('Calendario'[Fecha], "YYYY-MM")  -- para ordenar
Nombres de mes en español 💡
Para el nombre del mes en español, usa FORMAT(fecha, 'MMMM') — DAX usará el idioma del locale de tu modelo. Si el modelo está en inglés pero quieres los meses en español, necesitarás una tabla de traducción o usar SWITCH(MONTH(fecha), 1, 'Enero', 2, 'Febrero', ...).
⏱️

Calcular diferencias y desplazamientos

DATEDIFF(<fecha_inicio>, <fecha_fin>, <intervalo>)
Principiante — Intermedio
Calcula la diferencia entre dos fechas en el intervalo especificado. El intervalo puede ser: SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR.

Ejemplos:

// Antigüedad de un cliente en años
Antigüedad Años = DATEDIFF('Clientes'[FechaAlta], TODAY(), YEAR)

// Días desde el último pedido
Días Sin Compra = DATEDIFF(MAX('Pedidos'[Fecha]), TODAY(), DAY)

// Diferencia en meses (para análisis de cohortes)
Meses desde Alta = DATEDIFF('Clientes'[FechaAlta], MAX('Calendario'[Fecha]), MONTH)

// Horas de un proceso (timestamps con hora)
Duración Horas = DATEDIFF('Procesos'[Inicio], 'Procesos'[Fin], HOUR)
EDATE(<fecha_inicio>, <meses>)
Intermedio
Devuelve la fecha que resulta de sumar N meses a una fecha. Gestiona automáticamente el fin de mes (si la fecha es el 31 y el mes destino tiene menos días, devuelve el último día del mes).

Ejemplos:

// Fecha de renovación (12 meses después del contrato)
Fecha Renovación = EDATE('Contratos'[FechaInicio], 12)

// Hace 3 meses:
Hace 3 Meses = EDATE(TODAY(), -3)

// Hace 1 año:
Hace 1 Año = EDATE(TODAY(), -12)
EOMONTH(<fecha_inicio>, <meses>)
Principiante — Intermedio
Devuelve el ÚLTIMO DÍA del mes resultante de sumar N meses. Esencial para cálculos de períodos completos y cierres de mes.

Ejemplos:

// Último día del mes actual
Fin Mes Actual = EOMONTH(TODAY(), 0)

// Último día del mes anterior
Fin Mes Anterior = EOMONTH(TODAY(), -1)

// Primer día del mes actual (truco con EOMONTH):
Inicio Mes Actual = EOMONTH(TODAY(), -1) + 1
// El último día del mes anterior + 1 = primer día del mes actual

// Último día del mes siguiente:
Fin Mes Siguiente = EOMONTH(TODAY(), 1)

// Patrón para filtrar el mes actual completo:
Es Mes Actual =
AND(
    'Calendario'[Fecha] >= DATE(YEAR(TODAY()), MONTH(TODAY()), 1),
    'Calendario'[Fecha] <= EOMONTH(TODAY(), 0)
)
NETWORKDAYS(<fecha_inicio>, <fecha_fin>, [<festivos>])
Intermedio
Cuenta los días laborables (lunes a viernes) entre dos fechas, excluyendo opcionalmente una tabla de días festivos. Perfecta para SLAs, plazos y análisis de productividad.

Ejemplos:

// Días laborables para resolver un ticket de soporte
Días Lab Resolución = NETWORKDAYS('Tickets'[FechaApertura], 'Tickets'[FechaCierre])

// Con tabla de festivos:
Días Lab con Festivos =
NETWORKDAYS(
    'Tickets'[FechaApertura],
    'Tickets'[FechaCierre],
    'Festivos'[Fecha]  -- tabla con columna de fechas festivas
)

// SLA: ¿cuántos tickets se resolvieron en <= 3 días laborables?
Tickets en SLA =
COUNTROWS(
    FILTER('Tickets',
        NETWORKDAYS('Tickets'[FechaApertura], 'Tickets'[FechaCierre]) <= 3)
)
YEARFRAC(<fecha_inicio>, <fecha_fin>, [<base>])
Intermedio
Devuelve la fracción de año entre dos fechas (ej: 0.5 = 6 meses). Útil para cálculos financieros de intereses y proporciones temporales precisas.

Ejemplo:

// Antigüedad exacta en años (con decimales)
Antigüedad Exacta = YEARFRAC('Clientes'[FechaAlta], TODAY(), 1)
// 2.75 = 2 años y 9 meses de antigüedad
📌

TODAY, NOW, UTCTODAY, UTCNOW

TODAY() | NOW() | UTCTODAY() | UTCNOW()
Principiante
  • TODAY() — Fecha actual sin hora (se actualiza en cada refresco)
  • NOW() — Fecha y hora actuales
  • UTCTODAY() — Fecha actual en UTC
  • UTCNOW() — Fecha y hora actuales en UTC

Ejemplo — Métricas relativas al día de hoy:

// Ventas de los últimos 30 días (ventana móvil)
Ventas Últimos 30 días =
CALCULATE(
    SUM('Ventas'[Importe]),
    DATESINPERIOD('Calendario'[Fecha], TODAY(), -30, DAY)
)

// ¿El pedido está vencido?
'Pedidos'[Vencido] = IF('Pedidos'[FechaEntrega] < TODAY(), TRUE, FALSE)

// Días hasta la próxima renovación
Días Para Renovar = DATEDIFF(TODAY(), 'Contratos'[FechaRenovación], DAY)
⚠️ Funciones volátiles
TODAY() y NOW() son funciones VOLÁTILES — se recalculan en cada refresco del informe. Esto está bien para métricas dinámicas, pero si necesitas la fecha de un proceso concreto (ej: cuándo se importaron los datos), guárdala en una columna de la tabla en lugar de usar NOW().
🗓️

Construir tu tabla de calendario

CALENDAR(<fecha_inicio>, <fecha_fin>)
Intermedio
Devuelve una tabla de una columna ([Date]) con todas las fechas entre las dos indicadas (inclusivo). La base para crear una tabla de calendario personalizada.

Ejemplos:

// Tabla calculada con todas las fechas de 2020 a 2030
Calendario =
CALENDAR(DATE(2020,1,1), DATE(2030,12,31))

// Versión dinámica (siempre 5 años atrás y 2 años adelante):
Calendario Dinámico =
CALENDAR(
    DATE(YEAR(TODAY()) - 5, 1, 1),
    DATE(YEAR(TODAY()) + 2, 12, 31)
)

// Añadir columnas con ADDCOLUMNS:
Calendario Completo =
ADDCOLUMNS(
    CALENDAR(DATE(2020,1,1), DATE(2030,12,31)),
    "Año", YEAR([Date]),
    "Mes", MONTH([Date]),
    "Nombre Mes", FORMAT([Date], "MMMM"),
    "Trimestre", "Q" & QUARTER([Date]),
    "Semana", WEEKNUM([Date], 21),
    "Es Fin Semana", WEEKDAY([Date], 2) >= 6,
    "Año-Mes", FORMAT([Date], "YYYY-MM"),
    "Año-Trimestre", FORMAT([Date], "YYYY") & "-Q" & QUARTER([Date])
)
CALENDARAUTO([<fin_de_mes_fiscal>])
Principiante — Intermedio
Genera automáticamente una tabla de calendario basándose en el rango de fechas de todas las tablas del modelo. ¡No necesitas definir el rango manualmente! DAX busca la fecha más antigua y más reciente en todo el modelo.

Ejemplos:

// ¡Una sola línea! DAX detecta el rango de fechas automáticamente
Calendario = CALENDARAUTO()

// Con año fiscal que termina en marzo (mes 3):
Calendario Fiscal = CALENDARAUTO(3)
// Genera hasta el 31 de marzo del último año con datos
CALENDAR vs CALENDARAUTO 💡
CALENDARAUTO es perfecta para empezar rápido. Pero para modelos en producción, CALENDAR con fechas explícitas es más recomendable — así tienes control total sobre el rango y no dependes de cómo cambien los datos.
🛠️

Los patrones de fecha más usados en proyectos reales

Patrón 1 — Tabla de calendario completa (tabla calculada):

Calendario =
ADDCOLUMNS(
    CALENDARAUTO(),
    "Año", YEAR([Date]),
    "Mes Num", MONTH([Date]),
    "Mes Nombre", FORMAT([Date], "MMMM"),
    "Trimestre Num", QUARTER([Date]),
    "Trimestre", "Q" & QUARTER([Date]),
    "Semana ISO", WEEKNUM([Date], 21),
    "Día Semana Num", WEEKDAY([Date], 2),
    "Día Semana Nombre", FORMAT([Date], "DDDD"),
    "Es Fin Semana", IF(WEEKDAY([Date], 2) >= 6, TRUE, FALSE),
    "Año-Mes", FORMAT([Date], "YYYY-MM"),
    "Año-Trim", FORMAT([Date], "YYYY") & "-Q" & QUARTER([Date]),
    "Mes-Año", FORMAT([Date], "MMM YYYY"),
    "Es Mes Actual", MONTH([Date]) = MONTH(TODAY()) && YEAR([Date]) = YEAR(TODAY()),
    "Es Año Actual", YEAR([Date]) = YEAR(TODAY()),
    "Días hasta Hoy", DATEDIFF([Date], TODAY(), DAY)
)

Patrón 2 — Filtro dinámico de período reciente:

// Últimos N días (parámetro configurable)
Ventas Período =
VAR DiasAtras = SELECTEDVALUE('Selector Días'[Valor], 30)
RETURN
CALCULATE(
    SUM('Ventas'[Importe]),
    DATESINPERIOD('Calendario'[Fecha], TODAY(), -DiasAtras, DAY)
)

Patrón 3 — Columna de antigüedad en segmentos:

Segmento Antigüedad =
VAR Dias = DATEDIFF('Clientes'[FechaAlta], TODAY(), DAY)
RETURN
SWITCH(TRUE(),
    Dias <= 30, "Nuevo (< 1 mes)",
    Dias <= 90, "Reciente (1-3 meses)",
    Dias <= 365, "Establecido (3-12 meses)",
    Dias <= 730, "Fiel (1-2 años)",
    "Veterano (> 2 años)"
)
🚀 Siguiente paso
¡Ahora manejas las fechas como una pro! El siguiente nivel es la Inteligencia de Tiempo — funciones específicas para comparativas YTD, vs año anterior y medias móviles con tablas de calendario marcadas.