Files
fn_registry/python/functions/infra/expand_rrule.md
T
egutierrez 1c8a86594f feat(dav): expand_rrule + dav_make_calendar para recurrencia y multi-calendario
Dos funciones nuevas del grupo de capacidad `dav`:
- expand_rrule_py_infra (pure): expande una RRULE iCalendar a las fechas de
  cada ocurrencia dentro de un rango [from, to]. Solo stdlib (datetime, re).
  Soporta FREQ DAILY/WEEKLY/MONTHLY/YEARLY, INTERVAL, COUNT, UNTIL, BYDAY. 9 tests.
- dav_make_calendar_py_infra (impure): crea una coleccion de calendario nueva
  via MKCALENDAR + PROPPATCH de nombre/color. Idempotente si ya existe. 11 tests.

Consumidas por la app osint_web (eventos recurrentes + creacion de agendas).
Pagina del grupo dav actualizada con ambas.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 23:30:01 +02:00

4.3 KiB

name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, file_path, params, output
name kind lang domain version purity signature description tags uses_functions uses_types returns returns_optional error_type imports tested tests test_file_path file_path params output
expand_rrule function py infra 1.0.0 pure def expand_rrule(dtstart_ical: str, rrule: str, range_start: str, range_end: str, all_day: bool = False) -> list[str] Expande una RRULE iCalendar a la lista ordenada de fechas DTSTART de cada ocurrencia que cae dentro de un rango [range_start, range_end]. Pura, determinista, solo stdlib (sin python-dateutil). Soporta FREQ DAILY/WEEKLY/MONTHLY/YEARLY, INTERVAL, COUNT, UNTIL y BYDAY (para WEEKLY).
dav
calendar
ical
rrule
recurrence
caldav
false
true
test_golden_weekly_count_4
test_edge_monthly_interval_2
test_edge_weekly_byday_two_days
test_edge_all_day_vs_with_time
test_until_recorta
test_filtro_por_rango_excluye_fuera
test_dtstart_anterior_al_rango_pero_serie_entra
test_componente_no_soportado_se_ignora
test_sin_count_ni_until_acota_a_range_end
python/functions/infra/expand_rrule_test.py python/functions/infra/expand_rrule.py
name desc
dtstart_ical Fecha de inicio del evento maestro en formato iCal crudo: YYYYMMDD (all-day), YYYYMMDDTHHMMSS o YYYYMMDDTHHMMSSZ. Es la primera ocurrencia (la serie la incluye si cae en rango).
name desc
rrule Cuerpo de la RRULE SIN el prefijo 'RRULE:', p.ej. 'FREQ=WEEKLY;INTERVAL=1;COUNT=10' o 'FREQ=MONTHLY;UNTIL=20261231;BYDAY=MO,WE'.
name desc
range_start Limite inferior del rango como YYYYMMDD (inclusive). Solo se devuelven ocurrencias cuya fecha YYYYMMDD del DTSTART cae en [range_start, range_end].
name desc
range_end Limite superior del rango como YYYYMMDD (inclusive). Tambien acota la generacion cuando faltan COUNT y UNTIL en la RRULE.
name desc
all_day Si True las ocurrencias se devuelven como YYYYMMDD; si False conservan la parte de hora del dtstart original (misma hora local en cada ocurrencia) y devuelven YYYYMMDDTHHMMSS sin sufijo Z. Default False.
Lista ordenada de strings DTSTART iCal, una por ocurrencia en rango. Lista vacia si la RRULE no produce ninguna en [range_start, range_end].

Ejemplo

import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from infra.expand_rrule import expand_rrule

# Reunion semanal los lunes a las 09:00, 4 ocurrencias, ventana de enero 2026.
fechas = expand_rrule(
    "20260105T090000",
    "FREQ=WEEKLY;COUNT=4",
    "20260101",
    "20261231",
)
print(fechas)
# ['20260105T090000', '20260112T090000', '20260119T090000', '20260126T090000']

# Evento all-day mensual cada 2 meses, solo las que caen en el primer semestre.
fechas = expand_rrule(
    "20260115",
    "FREQ=MONTHLY;INTERVAL=2;COUNT=4",
    "20260101",
    "20260630",
    all_day=True,
)
print(fechas)
# ['20260115', '20260315', '20260515']

Cuando usarla

Cuando un cliente CalDAV necesita mostrar las ocurrencias de un evento recurrente dentro de la ventana visible del calendario: tienes el DTSTART y la RRULE del VEVENT maestro y quieres la lista concreta de fechas de inicio que caen entre dos limites para pintarlas en la agenda. Tambien para contar o iterar instancias de una serie sin instanciar todo el iCal.

Gotchas

  • No implementa el RFC 5545 completo. Componentes soportados:
    • FREQ (obligatorio): DAILY, WEEKLY, MONTHLY, YEARLY.
    • INTERVAL (default 1).
    • COUNT (incluye la primera ocurrencia = dtstart).
    • UNTIL (YYYYMMDD o YYYYMMDDTHHMMSSZ, inclusive).
    • BYDAY solo para FREQ=WEEKLY (MO,TU,WE,TH,FR,SA,SU).
  • Cualquier otro componente (BYMONTHDAY, BYSETPOS, BYMONTH, WKST avanzado, EXDATE, RDATE, etc.) se ignora silenciosamente — no falla, pero el resultado puede diferir del esperado por el RFC en esos casos.
  • Si faltan COUNT y UNTIL a la vez, la generacion se acota por range_end con un tope de seguridad duro de 1000 ocurrencias para no colgar.
  • En FREQ=MONTHLY/YEARLY con dia 29/30/31, los meses sin ese dia recortan al ultimo dia valido del mes destino.
  • No gestiona zonas horarias: con all_day=False conserva la hora local del dtstart sin sufijo Z; el llamador es responsable de la tz (TZID/VTIMEZONE).
  • El filtro de rango compara solo la parte YYYYMMDD del DTSTART, no la hora.