¿Qué es un Archivo ICS?
Un archivo ICS — que usa el formato iCalendar — es un archivo de texto plano que codifica eventos de calendario, tareas pendientes e información de disponibilidad en un formato estandarizado definido por el RFC 5545. La extensión .ics (y la más antigua .ical) es reconocida por todas las aplicaciones de calendario principales: Google Calendar, Apple Calendario, Microsoft Outlook, Thunderbird/Lightning y docenas de aplicaciones móviles.
El formato sirve para dos propósitos principales: el intercambio estático (compartir un solo evento o un lote de eventos adjuntando un archivo .ics a un correo electrónico o un enlace de descarga) y las suscripciones en vivo (publicar una URL de calendario a la que otros se suscriben y que se actualiza automáticamente).
Estructura del Archivo iCalendar
Un archivo ICS es una secuencia de componentes, cada uno siguiendo el patrón BEGIN:NOMBRE_COMPONENTE … END:NOMBRE_COMPONENTE. El contenedor externo siempre es VCALENDAR. Dentro encontramos componentes como VEVENT, VTODO, VJOURNAL, VTIMEZONE y VALARM.
ICS Mínimo con un Solo Evento
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//MiApp//MiApp 1.0//ES
BEGIN:VEVENT
UID:20240415T120000Z-001@miapp.com
DTSTAMP:20240415T120000Z
DTSTART:20240520T140000Z
DTEND:20240520T150000Z
SUMMARY:Reunión de Arranque de Proyecto
DESCRIPTION:Discutir hoja de ruta Q3 y asignar tareas.
LOCATION:Sala 4B / Zoom https://zoom.us/j/1234
ORGANIZER;CN=Ana García:mailto:ana@ejemplo.com
ATTENDEE;CN=Carlos López;RSVP=TRUE:mailto:carlos@ejemplo.com
STATUS:CONFIRMED
END:VEVENT
END:VCALENDAR
ICS Completo con Zona Horaria y Alarma
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Google Inc//Google Calendar 70.9054//EN
CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VTIMEZONE
TZID:Europe/Madrid
BEGIN:STANDARD
DTSTART:19701025T030000
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19700329T020000
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:abc123@google.com
DTSTAMP:20240401T090000Z
DTSTART;TZID=Europe/Madrid:20240515T100000
DTEND;TZID=Europe/Madrid:20240515T110000
SUMMARY:Revisión de Sprint del Equipo
RRULE:FREQ=WEEKLY;BYDAY=MI;COUNT=8
BEGIN:VALARM
TRIGGER:-PT15M
ACTION:DISPLAY
DESCRIPTION:Recordatorio: Revisión de Sprint en 15 minutos
END:VALARM
END:VEVENT
END:VCALENDAR
Propiedades Clave Explicadas
Propiedades de VEVENT
| Propiedad | Descripción | Ejemplo |
|---|---|---|
UID |
Identificador único global del evento | uid001@ejemplo.com |
DTSTAMP |
Cuándo se creó el evento (UTC) | 20240415T120000Z |
DTSTART |
Fecha/hora de inicio del evento | 20240520T140000Z |
DTEND |
Fecha/hora de fin del evento | 20240520T150000Z |
SUMMARY |
Título del evento | Reunión de Equipo |
DESCRIPTION |
Descripción completa | Texto multilinea |
LOCATION |
Ubicación física o virtual | Sala 4B |
RRULE |
Regla de recurrencia | FREQ=WEEKLY;BYDAY=LU,MI,VI |
STATUS |
Estado del evento | CONFIRMED, TENTATIVE, CANCELLED |
Reglas de Recurrencia (RRULE)
RRULE es la parte más potente y compleja de iCalendar. Define eventos repetidos usando una combinación de frecuencia, intervalo, restricciones de día y recuento o fecha de fin.
# Todos los días laborables
RRULE:FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR
# Cada dos semanas los lunes y miércoles
RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,WE
# El primer lunes de cada mes
RRULE:FREQ=MONTHLY;BYDAY=1MO
# Cada año el 15 de junio — cumpleaños recurrente
RRULE:FREQ=YEARLY;BYMONTH=6;BYMONTHDAY=15
# Todos los martes, 10 veces en total
RRULE:FREQ=WEEKLY;BYDAY=TU;COUNT=10
# Todos los lunes hasta el 31 de diciembre de 2024
RRULE:FREQ=WEEKLY;BYDAY=MO;UNTIL=20241231T235959Z
Valores de BYDAY en español: MO (lunes), TU (martes), WE (miércoles), TH (jueves), FR (viernes), SA (sábado), SU (domingo). Prefija con un número para "el N-ésimo día de la semana": 1MO = primer lunes, -1FR = último viernes.
Codificación de Fecha y Hora
iCalendar soporta tres representaciones de fecha/hora:
UTC: Añade Z al final para indicar UTC. DTSTART:20240520T140000Z significa las 14:00 UTC independientemente de la zona horaria del receptor.
Hora local con TZID: DTSTART;TZID=Europe/Madrid:20240520T140000 significa las 14:00 en Madrid. El TZID debe coincidir con un componente VTIMEZONE en el mismo archivo, o ser un nombre de zona horaria IANA que la aplicación receptora conozca.
Todo el día: Usa el formato solo fecha sin componente de hora: DTSTART;VALUE=DATE:20240520
Trabajar con ICS en Python
pip install icalendar pytz
Leer un Archivo ICS
from icalendar import Calendar
def leer_ics(ruta: str) -> list[dict]:
"""Analiza un archivo ICS y devuelve una lista de dicts de eventos."""
eventos = []
with open(ruta, 'rb') as f:
cal = Calendar.from_ical(f.read())
for componente in cal.walk():
if componente.name == 'VEVENT':
inicio = componente.get('DTSTART').dt
eventos.append({
'uid': str(componente.get('UID', '')),
'titulo': str(componente.get('SUMMARY', 'Sin título')),
'inicio': inicio,
'lugar': str(componente.get('LOCATION', '')),
})
return eventos
for ev in leer_ics('reuniones.ics'):
print(f"{ev['inicio']} — {ev['titulo']}")
Crear un Archivo ICS
from icalendar import Calendar, Event, Alarm
from datetime import datetime, timedelta
import pytz, uuid
def crear_evento_ics(titulo, inicio, duracion_min, lugar='', alarma_min=15):
cal = Calendar()
cal.add('prodid', '-//MiApp//ES')
cal.add('version', '2.0')
evento = Event()
evento.add('uid', str(uuid.uuid4()))
evento.add('dtstamp', datetime.now(pytz.utc))
evento.add('dtstart', inicio)
evento.add('dtend', inicio + timedelta(minutes=duracion_min))
evento.add('summary', titulo)
if lugar:
evento.add('location', lugar)
alarma = Alarm()
alarma.add('action', 'DISPLAY')
alarma.add('description', f'Recordatorio: {titulo}')
alarma.add('trigger', timedelta(minutes=-alarma_min))
evento.add_component(alarma)
cal.add_component(evento)
return cal.to_ical().decode('utf-8')
madrid = pytz.timezone('Europe/Madrid')
inicio = madrid.localize(datetime(2024, 6, 15, 10, 0, 0))
ics = crear_evento_ics('Revisión de Sprint', inicio, 90, 'Sala A', 15)
with open('reunion.ics', 'wb') as f:
f.write(ics.encode('utf-8'))
Importar y Suscribirse a ICS
Importar (una sola vez)
Google Calendar: calendar.google.com → Configuración → Importar y exportar → Importar → subir el archivo .ics
Apple Calendario (macOS): Haz doble clic en el archivo .ics. Calendario te pregunta en qué calendario añadir los eventos.
Outlook: Archivo → Abrir y exportar → Importar/Exportar → Importar un iCalendar (.ics)
Suscribirse (fuente en vivo)
Una suscripción de calendario es una URL que apunta a un archivo ICS que se actualiza periódicamente. La aplicación suscriptora sondea la URL cada pocas horas y actualiza los eventos automáticamente.
Google Calendar: Otros calendarios (botón +) → Desde URL → pegar la URL ICS → Añadir calendario
Apple Calendario: Archivo → Nueva suscripción de calendario → introducir URL
Problemas Comunes y Soluciones
UID ausente — Cada VEVENT debe tener un UID único globalmente. Sin él, algunas aplicaciones rechazan la importación o descartan silenciosamente el evento.
Discrepancias de zona horaria — Usar TZID=America/New_York sin incluir un componente VTIMEZONE puede confundir a las aplicaciones receptoras. Usa marcas de tiempo UTC (añadiendo Z) o incluye un bloque VTIMEZONE completo.
Longitud de línea — El RFC 5545 especifica que las líneas no deben superar los 75 octetos. Las líneas más largas deben "plegarse" insertando un CRLF seguido de un espacio. La biblioteca icalendar de Python gestiona el plegado automáticamente.
Usa el validador en línea icalendar.org/validator para comprobar los archivos ICS antes de distribuirlos.
Conversiones relacionadas
Conversiones frecuentes del catálogo: