diff --git a/AKModel/models.py b/AKModel/models.py
index 86fa8e15301e0c3c1539215761e30106e2210c82..3a91facb1319eb7b5b2c8d6850143888770615f0 100644
--- a/AKModel/models.py
+++ b/AKModel/models.py
@@ -1,6 +1,7 @@
 import itertools
 import json
-from datetime import timedelta
+from datetime import datetime, timedelta
+from typing import Iterable
 
 from django.db import models
 from django.apps import apps
@@ -163,67 +164,91 @@ class Event(models.Model):
                 .filter(availabilities__count=0, owners__count__gt=0)
                 )
 
-    def time_slots(self, *, slots_in_an_hour=1.0):
+    def _generate_slots_from_block(
+        self, start: datetime, end: datetime, slot_duration: timedelta, slot_index: int = 0
+    ) -> Iterable[list[int, "Availability"]]:
         from AKModel.availability.models import Availability
 
-        rooms = Room.objects.filter(event=self)
-        slot_duration = timedelta(hours=(1.0 / slots_in_an_hour))
-        slot_index = 0
-        current_slot = self.start
-        current_block = []
-        previous_slot = None
-
-        room_availabilities = list({availability
-                               for room in rooms
-                               for availability in room.availabilities.all()})
+        current_slot_start = start
+        previous_slot_start: datetime | None = None
 
-        while current_slot < self.end:
-            slot = Availability(event=self,
-                                start=current_slot,
-                                end=current_slot + slot_duration)
+        current_block = []
 
-            if any((availability.contains(slot)
-                    for availability in room_availabilities)):
-                if previous_slot is not None and previous_slot + slot_duration < current_slot:
+        room_availabilities = list({
+            availability
+            for room in Room.objects.filter(event=self)
+            for availability in room.availabilities.all()
+        })
+
+        while current_slot_start + slot_duration <= end:
+            slot = Availability(
+                event=self,
+                start=current_slot_start,
+                end=current_slot_start + slot_duration,
+            )
+
+            if any((availability.contains(slot) for availability in room_availabilities)):
+                # no gap in a block
+                if (
+                    previous_slot_start is not None
+                    and previous_slot_start + slot_duration < current_slot_start
+                ):
                     yield current_block
                     current_block = []
 
-                current_block.append(slot_index)
-                previous_slot = current_slot
+                current_block.append((slot_index, slot))
+                previous_slot_start = current_slot_start
 
             slot_index += 1
-            current_slot += slot_duration
+            current_slot_start += slot_duration
 
-        yield current_block
+        if current_block:
+            yield current_block
 
-    def time_slot(self, *, time_slot_index: int, slots_in_an_hour: float = 1.0) -> "Availability":
-        from AKModel.availability.models import Availability
-        slot_duration = timedelta(hours=(1.0 / slots_in_an_hour))
+        return slot_index
 
-        start = self.start + time_slot_index * slot_duration
+    def uniform_time_slots(self, *, slots_in_an_hour=1.0) -> Iterable[list[int, "Availability"]]:
+        yield from self._generate_slots_from_block(
+            start=self.start,
+            end=self.end,
+            slot_duration=timedelta(hours=(1.0 / slots_in_an_hour)),
+        )
 
-        return Availability(event=self,
-                            start=start,
-                            end=start + slot_duration)
+    def default_time_slots(self, *, slots_in_an_hour=1.0) -> Iterable[list[int, "Availability"]]:
+        slot_duration = timedelta(hours=(1.0 / slots_in_an_hour))
+        slot_index = 0
+
+        for block_slot in DefaultSlot.objects.filter(event=self).order_by("start", "end"):
+            # NOTE: We do not differentiate between different primary categories
+            slot_index = yield from self._generate_slots_from_block(
+                start=block_slot.start,
+                end=block_slot.end,
+                slot_duration=slot_duration,
+                slot_index=slot_index,
+            )
 
     def schedule_from_json(self, schedule: str) -> None:
         schedule = json.loads(schedule)
 
         slots_in_an_hour = schedule["input"]["timeslots"]["info"]["duration"]
 
+        timeslot_dict = {
+            slot_idx: slot
+            for block in self.default_time_slots(slots_in_an_hour=slots_in_an_hour)
+            for slot_idx, slot in block
+        }
+
         for scheduled_slot in schedule["scheduled_aks"]:
             slot = AKSlot.objects.get(id=int(scheduled_slot["ak_id"]))
             slot.room = Room.objects.get(id=int(scheduled_slot["room_id"]))
 
             scheduled_slot["timeslot_ids"] = list(map(int, scheduled_slot["timeslot_ids"]))
 
-            start = min(scheduled_slot["timeslot_ids"])
-            end = max(scheduled_slot["timeslot_ids"])
-
-            slot.start = self.time_slot(time_slot_index=start,
-                                        slots_in_an_hour=slots_in_an_hour).start
+            start_timeslot = timeslot_dict[min(scheduled_slot["timeslot_ids"])]
+            end_timeslot = timeslot_dict[max(scheduled_slot["timeslot_ids"])]
 
-            slot.duration = (end - start + 1) * timedelta(hours=(1.0 / slots_in_an_hour)).total_seconds() / 3600.0
+            slot.start = start_timeslot.start
+            slot.duration = (end_timeslot.end - start_timeslot.start).total_seconds() / 3600.0
             slot.save()
 
 class AKOwner(models.Model):
diff --git a/AKModel/views/ak.py b/AKModel/views/ak.py
index 90c3ef6e6f2163a3d2dfe9362a056893d6c06fc4..c65ae2661a6e0d1e1a811f4f3ca9c15e04250707 100644
--- a/AKModel/views/ak.py
+++ b/AKModel/views/ak.py
@@ -107,12 +107,10 @@ class AKJSONExportView(AdminViewMixin, FilterByEventSlugMixin, ListView):
         def _test_add_constraint(slot: Availability, availabilities: List[Availability]) -> bool:
             return _test_event_covered(slot, availabilities) and _test_slot_contained(slot, availabilities)
 
-        for block in self.event.time_slots(slots_in_an_hour=SLOTS_IN_AN_HOUR):
+        for block in self.event.default_time_slots(slots_in_an_hour=SLOTS_IN_AN_HOUR):
             current_block = []
 
-            for slot_index in block:
-                slot = self.event.time_slot(time_slot_index=slot_index,
-                                            slots_in_an_hour=SLOTS_IN_AN_HOUR)
+            for slot_index, slot in block:
                 time_constraints = []
 
                 if self.event.reso_deadline is None or slot.end < self.event.reso_deadline: