diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py
index c2037dc4ae2be3bbcb17275ab3eaa84c20b6d296..b0c487c952d68a8fec352a579a9bcd664da6d7c7 100644
--- a/aleksis/apps/chronos/models.py
+++ b/aleksis/apps/chronos/models.py
@@ -428,19 +428,7 @@ class Room(ExtensibleModel):
         ordering = ["name", "short_name"]
 
 
-class Lesson(ExtensibleModel):
-    subject = models.ForeignKey("Subject", on_delete=models.CASCADE, related_name="lessons")
-    teachers = models.ManyToManyField("core.Person", related_name="lessons_as_teacher")
-    periods = models.ManyToManyField("TimePeriod", related_name="lessons", through="LessonPeriod")
-    groups = models.ManyToManyField("core.Group", related_name="lessons")
-
-    date_start = models.DateField(verbose_name=_("Effective start date of lesson"), null=True)
-    date_end = models.DateField(verbose_name=_("Effective end date of lesson"), null=True)
-
-    @property
-    def teacher_names(self, sep: Optional[str] = ", ") -> str:
-        return sep.join([teacher.full_name for teacher in self.teachers.all()])
-
+class GroupPropertiesMixin:
     @property
     def group_names(self, sep: Optional[str] = ", ") -> str:
         return sep.join([group.short_name for group in self.groups.all()])
@@ -457,6 +445,22 @@ class Lesson(ExtensibleModel):
     def groups_to_show_names(self, sep: Optional[str] = ", ") -> str:
         return sep.join([group.short_name for group in self.groups_to_show])
 
+
+class TeacherPropertiesMixin:
+    @property
+    def teacher_names(self, sep: Optional[str] = ", ") -> str:
+        return sep.join([teacher.full_name for teacher in self.teachers.all()])
+
+
+class Lesson(ExtensibleModel, GroupPropertiesMixin, TeacherPropertiesMixin):
+    subject = models.ForeignKey("Subject", on_delete=models.CASCADE, related_name="lessons")
+    teachers = models.ManyToManyField("core.Person", related_name="lessons_as_teacher")
+    periods = models.ManyToManyField("TimePeriod", related_name="lessons", through="LessonPeriod")
+    groups = models.ManyToManyField("core.Group", related_name="lessons")
+
+    date_start = models.DateField(verbose_name=_("Effective start date of lesson"), null=True)
+    date_end = models.DateField(verbose_name=_("Effective end date of lesson"), null=True)
+
     def get_calendar_week(self, week: int):
         year = self.date_start.year
         if week < int(self.date_start.strftime("%V")):
@@ -1016,10 +1020,13 @@ class TimetableQuerySet(models.QuerySet):
 
 
 class EventQuerySet(DateRangeQuerySet, TimetableQuerySet):
-    pass
+    def annotate_day(self, day: date):
+        """ Annotate all events in the QuerySet with the provided date. """
 
+        return self.annotate(_date=models.Value(day, models.DateField()))
 
-class Event(ExtensibleModel):
+
+class Event(ExtensibleModel, GroupPropertiesMixin, TeacherPropertiesMixin):
     label_ = "event"
 
     objects = models.Manager.from_queryset(EventQuerySet)()
@@ -1042,6 +1049,22 @@ class Event(ExtensibleModel):
         else:
             return _("Event {}".format(self.pk))
 
+    @property
+    def period_from_on_day(self) -> int:
+        day = getattr(self, "_date", timezone.now().date())
+        if day != self.date_start:
+            return TimePeriod.period_min
+        else:
+            return self.period_from.period
+
+    @property
+    def period_to_on_day(self) -> int:
+        day = getattr(self, "_date", timezone.now().date())
+        if day != self.date_end:
+            return TimePeriod.period_max
+        else:
+            return self.period_to.period
+
     class Meta:
         ordering = ["date_start"]
         indexes = [models.Index(fields=["period_from", "period_to", "date_start", "date_end"])]
@@ -1064,10 +1087,10 @@ class ExtraLessonQuerySet(TimetableQuerySet):
         )
 
     def on_day(self, day:date):
-        self.within_dates(day, day)
+        return self.within_dates(day, day)
 
 
-class ExtraLesson(ExtensibleModel):
+class ExtraLesson(ExtensibleModel, GroupPropertiesMixin):
     label_ = "extra_lesson"
 
     objects = models.Manager.from_queryset(ExtraLessonQuerySet)()
diff --git a/aleksis/apps/chronos/templates/chronos/partials/event.html b/aleksis/apps/chronos/templates/chronos/partials/event.html
index b45ce48abc0d9414ba29220d935d25dc43a611ea..3ca850407977fd5a75a5e1b824086002c26943fc 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/event.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/event.html
@@ -13,7 +13,7 @@
     {# Teacher or class > Display rooms #}
     {% if type == "teacher" or type == "group" %}
       {% for room in event.rooms.all %}
-        {% include "chronos/partials/room.html" with room=room %}
+        {% include "chronos/partials/room.html" with room=room %}{% if not forloop.last %},{% endif %}
       {% endfor %}
     {% endif %}
 
diff --git a/aleksis/apps/chronos/templates/chronos/partials/room.html b/aleksis/apps/chronos/templates/chronos/partials/room.html
index d8985a8580219f78c693680fcc237d0e99391551..c96a7fd01975a86ac1bb66241a82872333d084bc 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/room.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/room.html
@@ -1,5 +1,7 @@
-<span class="tooltipped" data-position="bottom" data-tooltip="{{ room.name }}">
-  <a href="{% url "timetable" "room" room.pk %}">
-    {{ room.short_name }}
-  </a>
-</span>
+{% if room %}
+  <span class="tooltipped" data-position="bottom" data-tooltip="{{ room.name }}">
+    <a href="{% url "timetable" "room" room.pk %}">
+      {{ room.short_name }}
+    </a>
+  </span>
+{% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/colour.html b/aleksis/apps/chronos/templates/chronos/partials/subs/colour.html
index 61979467194ad62262a9c639d414db0dadefe260..833a24b2080d1ecb555ba94c8159fd8550ede7ce 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/colour.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/colour.html
@@ -6,4 +6,6 @@
   {% endif %}
 {% elif item.type == "supervision_substitution" %}
   blue-text
+{% elif item.type == "event" %}
+  purple-text
 {% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/comment.html b/aleksis/apps/chronos/templates/chronos/partials/subs/comment.html
new file mode 100644
index 0000000000000000000000000000000000000000..694a0ce3a430b25f295c2ed75120ef2e0ed9db84
--- /dev/null
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/comment.html
@@ -0,0 +1,5 @@
+{% if el.title %}
+  <em>{{ el.title }}</em>
+{% elif el.comment %}
+  <em>{{ el.comment }}</em>
+{% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/groups.html b/aleksis/apps/chronos/templates/chronos/partials/subs/groups.html
new file mode 100644
index 0000000000000000000000000000000000000000..d1a4da990b388458a9de5614acd17d988c4aac09
--- /dev/null
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/groups.html
@@ -0,0 +1,5 @@
+{% if type == "substitution" %}
+  {% include "chronos/partials/groups.html" with groups=el.lesson_period.lesson.groups.all %}
+{% elif type == "extra_lesson" or type == "event" %}
+  {% include "chronos/partials/groups.html" with groups=el.groups.all %}
+{% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/period.html b/aleksis/apps/chronos/templates/chronos/partials/subs/period.html
index 8e8d31bd2c48e14cc28330cc948e3d43882d6f2f..7d09cbebe5a320e98892ff637b99ae224e6f94f9 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/period.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/period.html
@@ -1,6 +1,14 @@
 <strong>
   {% if type == "substitution" %}
     {{ el.lesson_period.period.period }}.
+  {% elif type == "extra_lesson" %}
+    {{ el.period.period }}.
+  {% elif type == "event" %}
+    {% if el.period_from_on_day == el.period_to_on_day %}
+      {{ el.period_from_on_day }}.
+    {% else %}
+      {{ el.period_from_on_day }}.–{{ el.period_to_on_day }}.
+    {% endif %}
   {% elif type == "supervision_substitution" %}
     {% with break=el.supervision.break_item %}
       {{ break.after_period_number }}./{{ break.before_period_number }}.
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/room.html b/aleksis/apps/chronos/templates/chronos/partials/subs/room.html
index 7461e2dce94dae3200588ab54bb6033513fa5301..837aaa9c86b9cc695851b0116257f7edd48db00f 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/room.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/room.html
@@ -30,4 +30,10 @@
       {{ supervision.area.short_name }}
     </span>
   {% endwith %}
+{% elif type == "extra_lesson" %}
+  {% include "chronos/partials/room.html" with room=el.room %}
+{% elif type == "event" %}
+  {% for room in el.rooms.all %}
+    {% include "chronos/partials/room.html" with room=room %}{% if not forloop.last %},{% endif %}
+  {% endfor %}
 {% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html b/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html
index e6c9e86d016237276a2d5c894addba10c8a5942f..1210528a33f030c85fad581d47419ab41067e069 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html
@@ -15,14 +15,14 @@
     <strong>{{ el.subject.abbrev }}</strong>
   </span>
   {% elif el.subject and not el.lesson_period.lesson.subject %}
-    <span data-position="bottom" class="tooltipped" data-tooltip="{{ el.subject.name }}">
-    <strong>{{ el.subject.abbrev }}</strong>
-  </span>
+    {% include "chronos/partials/subject.html" with subject=el.subject %}
   {% else %}
-    <span data-position="bottom" class="tooltipped" data-tooltip="{{ el.lesson_period.lesson.subject.name }}">
-    <strong>{{ el.lesson_period.lesson.subject.abbrev }}</strong>
-  </span>
+    {% include "chronos/partials/subject.html" with subject=el.lesson_period.lesson.subject %}
   {% endif %}
 {% elif type == "supervision_substitution" %}
   {% trans "Supervision" %}
+{% elif type == "extra_lesson" %}
+  {% include "chronos/partials/subject.html" with subject=el.subject %}
+{% elif type == "event" %}
+  {% trans "Event" %}
 {% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html b/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html
index 4fbd63fea66282e17f77870ea3d16ec47fb31da4..4fa80d8fc7f82d8cae15010083ad4b93222bba41 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html
@@ -22,4 +22,6 @@
   <strong>
     {% include "chronos/partials/teachers.html" with teachers=el.teachers %}
   </strong>
+{% elif type == "extra_lesson" or type == "event" %}
+  {% include "chronos/partials/teachers.html" with teachers=el.teachers.all %}
 {% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/substitutions.html b/aleksis/apps/chronos/templates/chronos/substitutions.html
index eff5da58288939ad8906126ae80666b903f75074..3f5d70b3c8f29bbebb68fa7593b73a5f2291c7c0 100644
--- a/aleksis/apps/chronos/templates/chronos/substitutions.html
+++ b/aleksis/apps/chronos/templates/chronos/substitutions.html
@@ -61,9 +61,7 @@
       <tr class="{% include "chronos/partials/subs/colour.html" with item=item %}">
         {# TODO: Extend support for purple (events) #}
         <td>
-          {% if item.type == "substitution" %}
-            {% include "chronos/partials/groups.html" with groups=item.el.lesson_period.lesson.groups.all %}
-          {% endif %}
+          {% include "chronos/partials/subs/groups.html" with type=item.type el=item.el %}
         </td>
         <td>
          {% include "chronos/partials/subs/period.html" with type=item.type el=item.el %}
@@ -81,7 +79,7 @@
           <span class="hide-on-med-and-up">
             {% include "chronos/partials/subs/badge.html" with sub=item.el %}
           </span>
-          <em>{{ sub.comment|default:"" }}</em>
+          {% include "chronos/partials/subs/comment.html" with el=item.el %}
         </td>
         <td class="hide-on-small-and-down">
           {% include "chronos/partials/subs/badge.html" with sub=item.el %}
diff --git a/aleksis/apps/chronos/templates/chronos/substitutions_print.html b/aleksis/apps/chronos/templates/chronos/substitutions_print.html
index f52f4d58ae177721e23554daf673f031c472d323..56dcf953109aac69b4fcd2316c853e3fe1a61cb2 100644
--- a/aleksis/apps/chronos/templates/chronos/substitutions_print.html
+++ b/aleksis/apps/chronos/templates/chronos/substitutions_print.html
@@ -54,9 +54,7 @@
 
         <tr class="{% include "chronos/partials/subs/colour.html" with item=item %}">
           <td>
-            {% if item.type == "substitution" %}
-              {% include "chronos/partials/groups.html" with groups=item.el.lesson_period.lesson.groups.all %}
-            {% endif %}
+            {% include "chronos/partials/subs/groups.html" with type=item.type el=item.el %}
           </td>
           <td>
             {% include "chronos/partials/subs/period.html" with type=item.type el=item.el %}
@@ -72,7 +70,7 @@
           </td>
           <td>
             {% include "chronos/partials/subs/badge.html" with sub=item.el %}
-            <em>{{ sub.comment|default:"" }}</em>
+            {% include "chronos/partials/subs/comment.html" with el=item.el %}
           </td>
         </tr>
       {% endfor %}
diff --git a/aleksis/apps/chronos/util/build.py b/aleksis/apps/chronos/util/build.py
index 9e7754bb7c258d24a5bcbddfc89371a3904bb639..5a8ebfe8e38dc55345b84f209a6f664aa4e5da57 100644
--- a/aleksis/apps/chronos/util/build.py
+++ b/aleksis/apps/chronos/util/build.py
@@ -299,6 +299,35 @@ def build_substitutions_list(wanted_day: date) -> List[dict]:
         }
         rows.append(row)
 
+    # Get extra lessons
+    extra_lessons = ExtraLesson.objects.on_day(wanted_day)
+
+    for extra_lesson in extra_lessons:
+        row = {
+            "type": "extra_lesson",
+            "sort_a": "{}".format(extra_lesson.group_names),
+            "sort_b": "{}".format(extra_lesson.period.period),
+            "el": extra_lesson,
+        }
+        rows.append(row)
+
+    # Get events
+    events = Event.objects.on_day(wanted_day).annotate_day(wanted_day)
+
+    for event in events:
+        if event.groups.all():
+            sort_a = event.group_names
+        else:
+            sort_a = "Z.".format(event.teacher_names)
+
+        row = {
+            "type": "event",
+            "sort_a": sort_a,
+            "sort_b": "{}".format(event.period_from_on_day),
+            "el": event,
+        }
+        rows.append(row)
+
     # Sort all items
     def sorter(row: dict):
         return row["sort_a"] + row["sort_b"]