# How to Generate ICS Calendar Files from Text Using Python

> Generate ICS calendar files from text using Python. Learn how the awesome-llm-apps repository parses itineraries and converts them to iCalendar format with icalendar.

- Repository: [Shubham Saboo/awesome-llm-apps](https://github.com/shubhamsaboo/awesome-llm-apps)
- Tags: how-to-guide
- Published: 2026-02-16

---

**You can generate ICS calendar files from plain text by using the `generate_ics_content` function in the Shubhamsaboo/awesome-llm-apps repository, which parses day-based itineraries using regex and converts them into iCalendar format with the `icalendar` library.**

The ability to transform unstructured travel plans or schedules into downloadable calendar files is essential for AI-powered productivity tools. In the `awesome-llm-apps` repository, both the starter AI Travel Agent and the MCP AI Travel Planner implement a robust utility function that converts text itineraries into standard `.ics` files compatible with Google Calendar, Apple Calendar, and Outlook.

## Understanding the `generate_ics_content` Function

The core functionality resides in the `generate_ics_content` function, implemented in two locations within the repository:

- [`starter_ai_agents/ai_travel_agent/travel_agent.py`](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/starter_ai_agents/ai_travel_agent/travel_agent.py) (lines 23-60)
- [`mcp_ai_agents/ai_travel_planner_mcp_agent_team/app.py`](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/mcp_ai_agents/ai_travel_planner_mcp_agent_team/app.py) (lines 26-60)

This function accepts a string containing itinerary text and an optional start date, returning binary data representing a valid iCalendar file.

## Step-by-Step Process to Generate ICS from Text

### Initialize the Calendar Object

The function begins by creating an `icalendar.Calendar` instance and setting required properties:

```python
from icalendar import Calendar, Event
from datetime import datetime, timedelta
import re

cal = Calendar()
cal.add('prodid', '-//AI Travel Planner//EN')
cal.add('version', '2.0')

```

If no `start_date` is provided, the function defaults to `datetime.today()`.

### Parse the Itinerary Text with Regex

The function uses a regular expression to identify day-based entries in the text:

```python
day_pattern = re.compile(r'Day (\d+)[:\s]+(.*?)(?=Day \d+|$)', re.DOTALL)
days = day_pattern.findall(plan_text)

```

This pattern captures "Day 1:", "Day 2:", etc., along with the subsequent text until the next day marker or end of string.

### Create All-Day Events

For each matched day, the function creates an all-day calendar event:

```python
for day_num, day_content in days:
    event_date = start_date + timedelta(days=int(day_num) - 1)
    event = Event()
    event.add('summary', f'Day {day_num} Itinerary')
    event.add('description', day_content.strip())
    event.add('dtstart', event_date.date())
    event.add('dtend', (event_date + timedelta(days=1)).date())
    event.add('dtstamp', datetime.now())
    cal.add_component(event)

```

### Handle Unstructured Text Fallback

If the regex finds no day patterns, the function creates a single event containing the entire text:

```python
if not days:
    event = Event()
    event.add('summary', 'Travel Itinerary')
    event.add('description', plan_text)
    event.add('dtstart', start_date.date())
    event.add('dtend', (start_date + timedelta(days=1)).date())
    event.add('dtstamp', datetime.now())
    cal.add_component(event)

```

### Serialize to Binary Format

Finally, the function returns the binary iCalendar data:

```python
return cal.to_ical()

```

## Complete Code Example for Generating ICS Files

Here is a standalone implementation based on the repository's approach:

```python
from icalendar import Calendar, Event
from datetime import datetime, timedelta
import re

def generate_ics_content(plan_text: str, start_date: datetime = None) -> bytes:
    """Convert text itinerary to ICS calendar format."""
    if start_date is None:
        start_date = datetime.today()
    
    cal = Calendar()
    cal.add('prodid', '-//AI Travel Planner//EN')
    cal.add('version', '2.0')
    
    # Parse day-based entries

    day_pattern = re.compile(r'Day (\d+)[:\s]+(.*?)(?=Day \d+|$)', re.DOTALL)
    days = day_pattern.findall(plan_text)
    
    if days:
        for day_num, day_content in days:
            event_date = start_date + timedelta(days=int(day_num) - 1)
            event = Event()
            event.add('summary', f'Day {day_num} Itinerary')
            event.add('description', day_content.strip())
            event.add('dtstart', event_date.date())
            event.add('dtend', (event_date + timedelta(days=1)).date())
            event.add('dtstamp', datetime.now())
            cal.add_component(event)
    else:
        # Fallback for unstructured text

        event = Event()
        event.add('summary', 'Travel Itinerary')
        event.add('description', plan_text)
        event.add('dtstart', start_date.date())
        event.add('dtend', (start_date + timedelta(days=1)).date())
        event.add('dtstamp', datetime.now())
        cal.add_component(event)
    
    return cal.to_ical()

# Example usage

itinerary = """
Day 1: Arrive in Paris, check-in at Hotel Le Meurice.
Visit the Louvre (10:00-13:00). Lunch at Café Marly.

Day 2: Day trip to Versailles. Tour the palace (09:00-12:30).
Return to Paris, dinner at Le Jules Verne.
"""

ics_data = generate_ics_content(itinerary, datetime(2024, 9, 12))
with open("paris_trip.ics", "wb") as f:
    f.write(ics_data)

```

## Integrating ICS Generation into Streamlit Applications

The repository demonstrates how to expose this functionality in a web UI. After generating the itinerary, you can add a download button:

```python
import streamlit as st
from datetime import datetime

# Assuming generate_ics_content is defined and itinerary exists in session state

if st.session_state.itinerary:
    ics_content = generate_ics_content(
        st.session_state.itinerary,
        datetime.combine(start_date, datetime.min.time())
    )
    
    st.download_button(
        label="📅 Download Itinerary as Calendar (.ics)",
        data=ics_content,
        file_name="travel_itinerary.ics",
        mime="text/calendar"
    )

```

This implementation appears in [`travel_agent.py`](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/travel_agent.py) (lines 48-57) and [`app.py`](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/app.py) (lines 105-115), allowing users to export AI-generated travel plans directly into their personal calendar applications.

## Summary

- The `generate_ics_content` function in `Shubhamsaboo/awesome-llm-apps` converts text itineraries into standard `.ics` files using the `icalendar` library.
- The implementation uses regex pattern `r'Day (\d+)[:\s]+(.*?)(?=Day \d+|$)'` to parse day-based schedules, falling back to a single event for unstructured text.
- Each parsed day becomes an all-day calendar event with appropriate dates calculated from a configurable start date.
- The binary output integrates seamlessly with Streamlit's `st.download_button` for immediate user download.

## Frequently Asked Questions

### What is the ICS file format?

The ICS (iCalendar) format is a standard internet calendaring specification defined in RFC 5545. It uses a text-based structure to represent calendar events, to-do items, and journal entries. Files typically have the `.ics` extension and can be imported into Google Calendar, Microsoft Outlook, Apple Calendar, and other scheduling applications. The `icalendar` Python library provides an object-oriented interface for creating and manipulating these files.

### How does the regex pattern parse day entries?

The regex pattern `r'Day (\d+)[:\s]+(.*?)(?=Day \d+|$)'` uses a case-insensitive, dot-all approach to identify itinerary sections. The pattern captures the day number in the first group and the subsequent text in the second group, stopping when it encounters another "Day" heading or reaches the end of the string. The `re.DOTALL` flag ensures that multi-line descriptions are captured correctly, allowing day entries to span multiple lines in the input text.

### Can I modify the code for timed events instead of all-day events?

Yes, you can modify the event creation logic to support specific start and end times. Instead of passing date objects to `dtstart` and `dtend`, you would pass datetime objects. You would need to parse time information from your text input (e.g., "10:00-13:00") and combine it with the date. The current implementation in [`travel_agent.py`](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/travel_agent.py) and [`app.py`](https://github.com/Shubhamsaboo/awesome-llm-apps/blob/main/app.py) uses all-day events for simplicity, but the `icalendar` library fully supports timed events with timezone awareness.

### Which Python library is used for ICS generation?

The implementation relies on the `icalendar` library, a widely-used Python package for creating and parsing iCalendar files. This library provides the `Calendar` and `Event` classes used to construct the calendar structure, set properties like `prodid` and `version`, and serialize the final output using the `to_ical()` method. The library handles the proper formatting of dates, timestamps, and text encoding required by the iCalendar specification.