diff --git a/AKModel/admin.py b/AKModel/admin.py
index a90b3ff2686172b36f5c92a3000779decfa00263..cd3c6cd5082739f1e1ae38836ebd1929c5b2181c 100644
--- a/AKModel/admin.py
+++ b/AKModel/admin.py
@@ -2,6 +2,7 @@
 
 from django.contrib import admin
 
+from AKModel.availability import Availability
 from AKModel.models import Event, AKOwner, AKType, AKTrack, AKTag, AKRequirement, AK, Room, AKSlot
 
 admin.site.register(Event)
@@ -17,3 +18,5 @@ admin.site.register(AK)
 admin.site.register(Room)
 
 admin.site.register(AKSlot)
+
+admin.site.register(Availability)
diff --git a/AKModel/availability.py b/AKModel/availability.py
new file mode 100644
index 0000000000000000000000000000000000000000..7afa2fb9938f73d05bca36380dad695e127fc4bf
--- /dev/null
+++ b/AKModel/availability.py
@@ -0,0 +1,236 @@
+# This part of the code was adapted from pretalx (https://github.com/pretalx/pretalx)
+# Copyright 2017-2019, Tobias Kunze
+# Original Copyrights licensed under the Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0
+# Changes are marked in the code
+
+import datetime
+from typing import List
+
+from django.db import models
+from django.utils.functional import cached_property
+from django.utils.translation import gettext_lazy as _
+
+from AKModel.models import Event, AKOwner, Room, AK, AKType
+
+zero_time = datetime.time(0, 0)
+
+
+# CHANGES:
+# ScopeManager and LogMixin removed as they are not used in this project
+# adapted to event, people and room models
+# remove serialization as requirements are not covered
+# add translation
+# add meta class
+# enable availabilites for AKs and AKTypes
+# add verbose names and help texts to model attributes
+class Availability(models.Model):
+    """The Availability class models when people or rooms are available for.
+
+    The power of this class is not within its rather simple data model,
+    but with the operations available on it. An availability object can
+    span multiple days, but due to our choice of input widget, it will
+    usually only span a single day at most.
+    """
+    event = models.ForeignKey(
+        to=Event,
+        related_name='availabilities',
+        on_delete=models.CASCADE,
+        verbose_name=_('Event'),
+        help_text=_('Associated event'),
+    )
+    person = models.ForeignKey(
+        to=AKOwner,
+        related_name='availabilities',
+        on_delete=models.CASCADE,
+        null=True,
+        blank=True,
+        verbose_name=_('Person'),
+        help_text=_('Person whose availability this is'),
+    )
+    room = models.ForeignKey(
+        to=Room,
+        related_name='availabilities',
+        on_delete=models.CASCADE,
+        null=True,
+        blank=True,
+        verbose_name=_('Room'),
+        help_text=_('Room whose availability this is'),
+    )
+    ak = models.ForeignKey(
+        to=AK,
+        related_name='availabilities',
+        on_delete=models.CASCADE,
+        null=True,
+        blank=True,
+        verbose_name=_('AK'),
+        help_text=_('AK whose availability this is'),
+    )
+    ak_type = models.ForeignKey(
+        to=AKType,
+        related_name='availabilities',
+        on_delete=models.CASCADE,
+        null=True,
+        blank=True,
+        verbose_name=_('AK Type'),
+        help_text=_('AK Type whose availability this is'),
+    )
+    start = models.DateTimeField()
+    end = models.DateTimeField()
+
+    def __str__(self) -> str:
+        person = self.person.name if self.person else None
+        room = getattr(self.room, 'name', None)
+        event = getattr(getattr(self, 'event', None), 'name', None)
+        ak = getattr(self.ak, 'name', None)
+        ak_type = getattr(self.ak_type, 'name', None)
+        return f'Availability(event={event}, person={person}, room={room}, ak={ak}, ak type={ak_type})'
+
+    def __hash__(self):
+        return hash((getattr(self, 'event', None), self.person, self.room, self.ak, self.ak_type, self.start, self.end))
+
+    def __eq__(self, other: 'Availability') -> bool:
+        """Comparisons like ``availability1 == availability2``.
+
+        Checks if ``event``, ``person``, ``room``, ``ak``, ``ak_type``, ``start`` and ``end``
+        are the same.
+        """
+        return all(
+            [
+                getattr(self, attribute, None) == getattr(other, attribute, None)
+                for attribute in ['event', 'person', 'room', 'ak', 'ak_type', 'start', 'end']
+            ]
+        )
+
+    @cached_property
+    def all_day(self) -> bool:
+        """Checks if the Availability spans one (or, technically: multiple)
+        complete day."""
+        return self.start.time() == zero_time and self.end.time() == zero_time
+
+    def overlaps(self, other: 'Availability', strict: bool) -> bool:
+        """Test if two Availabilities overlap.
+
+        :param other:
+        :param strict: Only count a real overlap as overlap, not direct adjacency.
+        """
+
+        if not isinstance(other, Availability):
+            raise Exception('Please provide an Availability object')
+
+        if strict:
+            return (
+                    (self.start <= other.start < self.end)
+                    or (self.start < other.end <= self.end)
+                    or (other.start <= self.start < other.end)
+                    or (other.start < self.end <= other.end)
+            )
+        return (
+                (self.start <= other.start <= self.end)
+                or (self.start <= other.end <= self.end)
+                or (other.start <= self.start <= other.end)
+                or (other.start <= self.end <= other.end)
+        )
+
+    def contains(self, other: 'Availability') -> bool:
+        """Tests if this availability starts before and ends after the
+        other."""
+        return self.start <= other.start and self.end >= other.end
+
+    def merge_with(self, other: 'Availability') -> 'Availability':
+        """Return a new Availability which spans the range of this one and the
+        given one."""
+
+        if not isinstance(other, Availability):
+            raise Exception('Please provide an Availability object.')
+        if not other.overlaps(self, strict=False):
+            raise Exception('Only overlapping Availabilities can be merged.')
+
+        return Availability(
+            start=min(self.start, other.start), end=max(self.end, other.end)
+        )
+
+    def __or__(self, other: 'Availability') -> 'Availability':
+        """Performs the merge operation: ``availability1 | availability2``"""
+        return self.merge_with(other)
+
+    def intersect_with(self, other: 'Availability') -> 'Availability':
+        """Return a new Availability which spans the range covered both by this
+        one and the given one."""
+
+        if not isinstance(other, Availability):
+            raise Exception('Please provide an Availability object.')
+        if not other.overlaps(self, False):
+            raise Exception('Only overlapping Availabilities can be intersected.')
+
+        return Availability(
+            start=max(self.start, other.start), end=min(self.end, other.end)
+        )
+
+    def __and__(self, other: 'Availability') -> 'Availability':
+        """Performs the intersect operation: ``availability1 &
+        availability2``"""
+        return self.intersect_with(other)
+
+    @classmethod
+    def union(cls, availabilities: List['Availability']) -> List['Availability']:
+        """Return the minimal list of Availability objects which are covered by
+        at least one given Availability."""
+        if not availabilities:
+            return []
+
+        availabilities = sorted(availabilities, key=lambda a: a.start)
+        result = [availabilities[0]]
+        availabilities = availabilities[1:]
+
+        for avail in availabilities:
+            if avail.overlaps(result[-1], False):
+                result[-1] = result[-1].merge_with(avail)
+            else:
+                result.append(avail)
+
+        return result
+
+    @classmethod
+    def _pair_intersection(
+            cls,
+            availabilities_a: List['Availability'],
+            availabilities_b: List['Availability'],
+    ) -> List['Availability']:
+        """return the list of Availabilities, which are covered by each of the
+        given sets."""
+        result = []
+
+        # yay for O(b*a) time! I am sure there is some fancy trick to make this faster,
+        # but we're dealing with less than 100 items in total, sooo.. ¯\_(ツ)_/¯
+        for a in availabilities_a:
+            for b in availabilities_b:
+                if a.overlaps(b, True):
+                    result.append(a.intersect_with(b))
+
+        return result
+
+    @classmethod
+    def intersection(
+            cls, *availabilitysets: List['Availability']
+    ) -> List['Availability']:
+        """Return the list of Availabilities which are covered by all of the
+        given sets."""
+
+        # get rid of any overlaps and unmerged ranges in each set
+        availabilitysets = [cls.union(avialset) for avialset in availabilitysets]
+        # bail out for obvious cases (there are no sets given, one of the sets is empty)
+        if not availabilitysets:
+            return []
+        if not all(availabilitysets):
+            return []
+        # start with the very first set ...
+        result = availabilitysets[0]
+        for availset in availabilitysets[1:]:
+            # ... subtract each of the other sets
+            result = cls._pair_intersection(result, availset)
+        return result
+
+    class Meta:
+        verbose_name = _('Availability')
+        verbose_name_plural = _('Availabilities')
+        ordering = ['event', 'start']
diff --git a/AKModel/locale/de_DE/LC_MESSAGES/django.po b/AKModel/locale/de_DE/LC_MESSAGES/django.po
index 0f0eab2d64820f4b0bfcff6e906837a498839d2f..700d82e36966af0893c5dae80d50f98778bbc9c6 100644
--- a/AKModel/locale/de_DE/LC_MESSAGES/django.po
+++ b/AKModel/locale/de_DE/LC_MESSAGES/django.po
@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-10-12 12:40+0000\n"
+"POT-Creation-Date: 2019-10-12 14:29+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -11,8 +11,60 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: models.py:10 models.py:44 models.py:58 models.py:70 models.py:81
-#: models.py:95 models.py:136
+#: availability.py:38 models.py:18 models.py:31 models.py:81 models.py:122
+#: models.py:139 models.py:159
+msgid "Event"
+msgstr "Event"
+
+#: availability.py:39 models.py:32 models.py:82 models.py:123 models.py:140
+#: models.py:160
+msgid "Associated event"
+msgstr "Zugehöriges Event"
+
+#: availability.py:47
+msgid "Person"
+msgstr "Person"
+
+#: availability.py:48
+msgid "Person whose availability this is"
+msgstr "Person deren Verfügbarkeit hier abgebildet wird"
+
+#: availability.py:56 models.py:143 models.py:153
+msgid "Room"
+msgstr "Raum"
+
+#: availability.py:57
+msgid "Room whose availability this is"
+msgstr "Raum dessen Verfügbarkeit hier abgebildet wird"
+
+#: availability.py:65 models.py:126 models.py:152
+msgid "AK"
+msgstr "AK"
+
+#: availability.py:66
+#, fuzzy
+#| msgid "Availabilities"
+msgid "AK whose availability this is"
+msgstr "Verfügbarkeiten"
+
+#: availability.py:74 models.py:48
+msgid "AK Type"
+msgstr "AK Typ"
+
+#: availability.py:75
+msgid "AK Type whose availability this is"
+msgstr "AK Typ dessen Verfügbarkeit hier abgebildet wird"
+
+#: availability.py:234
+msgid "Availability"
+msgstr "Verfügbarkeit"
+
+#: availability.py:235
+msgid "Availabilities"
+msgstr "Verfügbarkeiten"
+
+#: models.py:10 models.py:44 models.py:56 models.py:68 models.py:79
+#: models.py:93 models.py:133
 msgid "Name"
 msgstr "Name"
 
@@ -52,11 +104,6 @@ msgstr "Aktiver Status"
 msgid "Marks currently active events"
 msgstr "Markiert aktuell aktive Events"
 
-#: models.py:18 models.py:31 models.py:83 models.py:125 models.py:144
-#: models.py:164
-msgid "Event"
-msgstr "Event"
-
 #: models.py:19
 msgid "Events"
 msgstr "Events"
@@ -85,7 +132,7 @@ msgstr "Instutution"
 msgid "Uni etc."
 msgstr "Universität o.ä."
 
-#: models.py:29 models.py:103
+#: models.py:29 models.py:101
 msgid "Web Link"
 msgstr "Internet Link"
 
@@ -93,10 +140,6 @@ msgstr "Internet Link"
 msgid "Link to Homepage"
 msgstr "Link zu Homepage oder Webseite"
 
-#: models.py:32 models.py:84 models.py:126 models.py:145 models.py:165
-msgid "Associated event"
-msgstr "Zugehöriges Event"
-
 #: models.py:35
 msgid "AK Owner"
 msgstr "AK Leitung"
@@ -109,234 +152,222 @@ msgstr "AK Leitungen"
 msgid "Name of the AK Type"
 msgstr "Name des AK Typs"
 
-#: models.py:45 models.py:59
+#: models.py:45 models.py:57
 msgid "Color"
 msgstr "Farbe"
 
-#: models.py:45 models.py:59
+#: models.py:45 models.py:57
 msgid "Color for displaying"
 msgstr "Farbe für die Anzeige"
 
-#: models.py:50
-msgid "AK Type"
-msgstr "AK Typ"
-
-#: models.py:51
+#: models.py:49
 msgid "AK Types"
 msgstr "AK Typen"
 
-#: models.py:58
+#: models.py:56
 msgid "Name of the AK Track"
 msgstr "Name des AK Tracks"
 
-#: models.py:62
+#: models.py:60
 msgid "AK Track"
 msgstr "AK Track"
 
-#: models.py:63
+#: models.py:61
 msgid "AK Tracks"
 msgstr "AK Tracks"
 
-#: models.py:70
+#: models.py:68
 msgid "Name of the AK Tag"
 msgstr "Name das AK Tags"
 
-#: models.py:73
+#: models.py:71
 msgid "AK Tag"
 msgstr "AK Tag"
 
-#: models.py:74
+#: models.py:72
 msgid "AK Tags"
 msgstr "AK Tags"
 
-#: models.py:81
+#: models.py:79
 msgid "Name of the Requirement"
 msgstr "Name der Anforderung"
 
-#: models.py:87
+#: models.py:85
 msgid "AK Requirement"
 msgstr "AK Anforderung"
 
-#: models.py:88
+#: models.py:86
 msgid "AK Requirements"
 msgstr "AK Anforderungen"
 
-#: models.py:95
+#: models.py:93
 msgid "Name of the AK"
 msgstr "Name des AKs"
 
-#: models.py:96
+#: models.py:94
 msgid "Short Name"
 msgstr "Kurzer Name"
 
-#: models.py:97
+#: models.py:95
 msgid "Name displayed in the schedule"
 msgstr "Name zur Anzeige im AK Plan"
 
-#: models.py:98
+#: models.py:96
 msgid "Description"
 msgstr "Beschreibung"
 
-#: models.py:98
+#: models.py:96
 msgid "Description of the AK"
 msgstr "Beschreibung des AKs"
 
-#: models.py:100
+#: models.py:98
 msgid "Owners"
 msgstr "Leitungen"
 
-#: models.py:100
+#: models.py:98
 msgid "Those organizing the AK"
 msgstr "Menschen, die den AK organisieren und halten"
 
-#: models.py:103
+#: models.py:101
 msgid "Link to wiki page"
 msgstr "Link zur Wiki Seite"
 
-#: models.py:105
+#: models.py:103
 msgid "Type"
 msgstr "Typ"
 
-#: models.py:105
+#: models.py:103
 msgid "Type of the AK"
 msgstr "Typ des AKs"
 
-#: models.py:106
+#: models.py:104
 msgid "Tags"
 msgstr "Tags"
 
-#: models.py:106
+#: models.py:104
 msgid "Tags provided by owners"
 msgstr "Tags, die durch die AK Leitung vergeben wurden"
 
-#: models.py:107
+#: models.py:105
 msgid "Track"
 msgstr "Track"
 
-#: models.py:108
+#: models.py:106
 msgid "Track the AK belongs to"
 msgstr "Track zu dem der AK gehört"
 
-#: models.py:110
+#: models.py:108
 msgid "Resolution Intention"
 msgstr "Resolutionsabsicht"
 
-#: models.py:111
+#: models.py:109
 msgid "Intends to submit a resolution"
 msgstr "Beabsichtigt eine Resolution einzureichen"
 
-#: models.py:112
+#: models.py:110
 msgid "Requirements"
 msgstr "Anforderungen"
 
-#: models.py:113
+#: models.py:111
 msgid "AK's Requirements"
 msgstr "Anforderungen des AKs"
 
-#: models.py:115
+#: models.py:113
 msgid "Conflicting AKs"
 msgstr "AK Konflikte"
 
-#: models.py:116
+#: models.py:114
 msgid "AKs that conflict and thus must not take place at the same time"
 msgstr "AKs, die Konflikte haben und deshalb nicht gleichzeitig stattfinden dürfen"
 
-#: models.py:117
+#: models.py:115
 msgid "Prerequisite AKs"
 msgstr "Vorausgesetzte AKs"
 
-#: models.py:118
+#: models.py:116
 msgid "AKs that should precede this AK in the schedule"
 msgstr "AKS die im AK Plan vor diesem AK stattfinden müssen"
 
-#: models.py:121
+#: models.py:118
 msgid "Internal Notes"
 msgstr "Interne Notizen"
 
-#: models.py:121
+#: models.py:118
 msgid "Notes to organizers"
 msgstr "Notizen an die Organisator*innen"
 
-#: models.py:123
+#: models.py:120
 msgid "Interest"
 msgstr "Interesse"
 
-#: models.py:123
+#: models.py:120
 msgid "Expected number of people"
 msgstr "Erwartete Personenzahl"
 
-#: models.py:129 models.py:157
-msgid "AK"
-msgstr "AK"
-
-#: models.py:130
+#: models.py:127
 msgid "AKs"
 msgstr "AKs"
 
-#: models.py:136
+#: models.py:133
 msgid "Name or number of the room"
 msgstr "Name oder Nummer des Raums"
 
-#: models.py:137
+#: models.py:134
 msgid "Building"
 msgstr "Gebäude"
 
-#: models.py:137
+#: models.py:134
 msgid "Name/number of the building"
 msgstr "Name oder Nummer des Gebäudes"
 
-#: models.py:138
+#: models.py:135
 msgid "Capacity"
 msgstr "Kapazität"
 
-#: models.py:138
+#: models.py:135
 msgid "Maximum number of people"
 msgstr "Maximale Personenzahl"
 
-#: models.py:139
+#: models.py:136
 msgid "Properties"
 msgstr "Eigenschaften"
 
-#: models.py:140
+#: models.py:137
 msgid "AK requirements fulfilled by the room"
 msgstr "AK Anforderungen, die dieser Raum erfüllt"
 
-#: models.py:148 models.py:158
-msgid "Room"
-msgstr "Raum"
-
-#: models.py:149
+#: models.py:144
 msgid "Rooms"
 msgstr "Räume"
 
-#: models.py:157
+#: models.py:152
 msgid "AK being mapped"
 msgstr "AK, der zugeordnet wird"
 
-#: models.py:159
+#: models.py:154
 msgid "Room the AK will take place in"
 msgstr "Raum in dem der AK stattfindet"
 
-#: models.py:160
+#: models.py:155
 msgid "Slot Begin"
 msgstr "Beginn des Slots"
 
-#: models.py:160
+#: models.py:155
 msgid "Time and date the slot begins"
 msgstr "Zeit und Datum zu der der AK beginnt"
 
-#: models.py:161
+#: models.py:156
 msgid "Duration"
 msgstr "Dauer"
 
-#: models.py:162
+#: models.py:157
 msgid "Length in hours"
 msgstr "Länge in Stunden"
 
-#: models.py:168
+#: models.py:163
 msgid "AK Slot"
 msgstr "AK Slot"
 
-#: models.py:169
+#: models.py:164
 msgid "AK Slots"
 msgstr "AK Slot"
diff --git a/AKModel/migrations/0007_availability.py b/AKModel/migrations/0007_availability.py
new file mode 100644
index 0000000000000000000000000000000000000000..9084fb78be045e71f696af01acaf1dae3e21877f
--- /dev/null
+++ b/AKModel/migrations/0007_availability.py
@@ -0,0 +1,41 @@
+# Generated by Django 2.2.6 on 2019-10-12 14:35
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ('AKModel', '0006_translation_akmodel'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Availability',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('start', models.DateTimeField()),
+                ('end', models.DateTimeField()),
+                ('ak', models.ForeignKey(blank=True, help_text='AK whose availability this is', null=True,
+                                         on_delete=django.db.models.deletion.CASCADE, related_name='availabilities',
+                                         to='AKModel.AK', verbose_name='AK')),
+                ('ak_type', models.ForeignKey(blank=True, help_text='AK Type whose availability this is', null=True,
+                                              on_delete=django.db.models.deletion.CASCADE,
+                                              related_name='availabilities', to='AKModel.AKType',
+                                              verbose_name='AK Type')),
+                ('event', models.ForeignKey(help_text='Associated event', on_delete=django.db.models.deletion.CASCADE,
+                                            related_name='availabilities', to='AKModel.Event', verbose_name='Event')),
+                ('person', models.ForeignKey(blank=True, help_text='Person whose availability this is', null=True,
+                                             on_delete=django.db.models.deletion.CASCADE, related_name='availabilities',
+                                             to='AKModel.AKOwner', verbose_name='Person')),
+                ('room', models.ForeignKey(blank=True, help_text='Room whose availability this is', null=True,
+                                           on_delete=django.db.models.deletion.CASCADE, related_name='availabilities',
+                                           to='AKModel.Room', verbose_name='Room')),
+            ],
+            options={
+                'verbose_name': 'Availability',
+                'verbose_name_plural': 'Availabilities',
+                'ordering': ['event', 'start'],
+            },
+        ),
+    ]
diff --git a/AKModel/models.py b/AKModel/models.py
index 54498b26512e123a4c400b1ecdb5bd1df7f94d3e..aad105de2feb217bfbe62173a7d863c62446d2b3 100644
--- a/AKModel/models.py
+++ b/AKModel/models.py
@@ -44,8 +44,6 @@ class AKType(models.Model):
     name = models.CharField(max_length=64, unique=True, verbose_name=_('Name'), help_text=_('Name of the AK Type'))
     color = models.CharField(max_length=7, blank=True, verbose_name=_('Color'), help_text=_('Color for displaying'))
 
-    # TODO model availability
-
     class Meta:
         verbose_name = _('AK Type')
         verbose_name_plural = _('AK Types')
@@ -116,7 +114,6 @@ class AK(models.Model):
                                        help_text=_('AKs that conflict and thus must not take place at the same time'))
     prerequisites = models.ManyToManyField(to='AK', blank=True, verbose_name=_('Prerequisite AKs'),
                                            help_text=_('AKs that should precede this AK in the schedule'))
-    # TODO model availability
 
     notes = models.TextField(blank=True, verbose_name=_('Internal Notes'), help_text=_('Notes to organizers'))
 
@@ -139,8 +136,6 @@ class Room(models.Model):
     properties = models.ManyToManyField(to=AKRequirement, verbose_name=_('Properties'),
                                         help_text=_('AK requirements fulfilled by the room'))
 
-    # TODO model availability
-
     event = models.ForeignKey(to=Event, on_delete=models.CASCADE, verbose_name=_('Event'),
                               help_text=_('Associated event'))