diff --git a/timetabling/timetable-ics.py b/timetabling/timetable-ics.py
index 51083c32f65f88d23184162432989aad87f231c1..64f8737a8ee58668ad31b00236485b8e90c0dc5d 100644
--- a/timetabling/timetable-ics.py
+++ b/timetabling/timetable-ics.py
@@ -5,13 +5,13 @@ import pytz
 import re
 import sys
 
-from ics import Calendar, Event
-from ics.grammar.parse import ContentLine
+from icalendar import Calendar, Event
 from datetime import datetime, timedelta
 
 from typing import Dict, Generator
 
-BASE_DATE = datetime(2024, 9, 23, 0, 0, 0, 0, pytz.timezone("Europe/London"))
+TIMEZONE = "Europe/London"
+BASE_DATE = datetime(2024, 9, 23, 0, 0, 0, 0, pytz.timezone(TIMEZONE))
 BEDFORD = 100 # not a year
 
 CAL_DAYS = [ "MO", "TU", "WE", "TH", "FR", "SA", "SU" ]
@@ -132,26 +132,26 @@ for ttfile in timetable_files:
                     start_time = get_time(week, offset, start)
                     end_time = get_end_time(start_time, duration_hours)
                     event = Event()
-                    event.name = name
-                    event.location = location
-                    event.begin = start_time
-                    event.end = end_time
+                    event.add("summary", name)
+                    event.add("location", location)
+                    event.add("dtstart", start_time)
+                    event.add("dtend", end_time)
                     for year in get_years(name):
-                        cals[year].events.add(event)
+                        cals[year].add_component(event)
                     if "bedford" in location.lower():
-                        event = event.clone()
+                        event = event.copy()
                         if "04-06" in location:
-                            event.name = f"ALL {name}"
+                            event.add("summary", f"ALL {name}")
                         elif "04" in location:
-                            event.name = f"4 {name}"
+                            event.add("summary", f"4 {name}")
                         elif "05" in location:
-                            event.name = f"5 {name}"
+                            event.add("summary", f"5 {name}")
                         elif "06" in location:
-                            event.name = f"6 {name}"
+                            event.add("summary", f"6 {name}")
                         else:
-                            event.name = f"? {name}"
+                            event.add("summary", f"? {name}")
 
-                        cals[BEDFORD].events.add(event)
+                        cals[BEDFORD].add_component(event)
         elif len(weeks) > 0:
             offset = get_day_offset(day)
             if offset < 0:
@@ -160,48 +160,47 @@ for ttfile in timetable_files:
                 start_time = get_time(weeks[0], offset, start)
                 end_time = get_end_time(start_time, duration_hours)
 
-                year_weeks = ",".join(map(
-                    lambda w: str(get_time(w, offset, start).isocalendar()[1]),
+                year_weeks = list(map(
+                    lambda w: get_time(w, offset, start).isocalendar()[1],
                     weeks
                 ))
 
                 event = Event()
-                event.name = name
-                event.location = location
-                event.begin = start_time
-                event.end = end_time
-                event.extra.append(ContentLine(
-                    name="RRULE",
-                    value="FREQ=YEARLY;"
-                        + f"COUNT={len(weeks)};"
-                        + f"BYWEEKNO={year_weeks};"
-                        + f"BYDAY={CAL_DAYS[offset]}"
-                ))
+                event.add("summary", name)
+                event.add("location", location)
+                event.add("dtstart", start_time)
+                event.add("dtend", end_time)
+                event.add("rrule", {
+                    "freq": "YEARLY",
+                    "count": len(weeks),
+                    "byweekno": year_weeks,
+                    "byday": CAL_DAYS[offset]
+                })
 
                 for year in get_years(name):
-                    cals[year].events.add(event)
+                    cals[year].add_component(event)
 
                 if "bedford" in location.lower():
-                    event = event.clone()
+                    event = event.copy()
                     if "04-06" in location:
-                        event.name = f"ALL {name}"
+                        event.add("summary", f"ALL {name}")
                     elif "04" in location:
-                        event.name = f"4 {name}"
+                        event.add("summary", f"4 {name}")
                     elif "05" in location:
-                        event.name = f"5 {name}"
+                        event.add("summary", f"5 {name}")
                     elif "06" in location:
-                        event.name = f"6 {name}"
+                        event.add("summary", f"6 {name}")
                     else:
-                        event.name = f"? {name}"
+                        event.add("summary", f"? {name}")
 
-                    cals[BEDFORD].events.add(event)
+                    cals[BEDFORD].add_component(event)
 
 
 for year in cals:
     if year != BEDFORD:
-        with open(f"Year{year}.ics", 'w') as icsfile:
-            icsfile.writelines(cals[year].serialize_iter())
+        with open(f"Year{year}.ics", 'wb') as icsfile:
+            icsfile.write(cals[year].to_ical())
 
-with open(f"Bedford.ics", 'w') as icsfile:
-    icsfile.writelines(cals[BEDFORD].serialize_iter())
+with open(f"Bedford.ics", 'wb') as icsfile:
+    icsfile.write(cals[BEDFORD].to_ical())