diff --git a/aleksis/apps/chronos/managers.py b/aleksis/apps/chronos/managers.py
index 4ca0ec807e491178b16384e4a671c64563753acf..0f2774cbca1d7d3291750a63841b9642b1a1549d 100644
--- a/aleksis/apps/chronos/managers.py
+++ b/aleksis/apps/chronos/managers.py
@@ -114,10 +114,8 @@ class LessonSubstitutionManager(CurrentSiteManager):
 
 
 class WeekQuerySetMixin:
-    def annotate_week(self, week: Union[CalendarWeek, int]):
+    def annotate_week(self, week: Union[CalendarWeek]):
         """Annotate all lessons in the QuerySet with the number of the provided calendar week."""
-        if isinstance(week, int):
-            week = CalendarWeek(week=week)
 
         return self.annotate(
             _week=models.Value(week.week, models.IntegerField()),
@@ -226,6 +224,7 @@ class LessonDataQuerySet(models.QuerySet, WeekQuerySetMixin):
             **{
                 self._subst_path + "teachers": teacher,
                 self._subst_path + "week": F("_week"),
+                self._subst_path + "year": F("_year"),
             }
         )
 
@@ -235,7 +234,11 @@ class LessonDataQuerySet(models.QuerySet, WeekQuerySetMixin):
         """Filter for all lessons taking part in a certain room."""
         qs1 = self.filter(**{self._period_path + "room": room})
         qs2 = self.filter(
-            **{self._subst_path + "room": room, self._subst_path + "week": F("_week"),}
+            **{
+                self._subst_path + "room": room,
+                self._subst_path + "week": F("_week"),
+                self._subst_path + "year": F("_year"),
+            }
         )
 
         return qs1.union(qs2)
@@ -303,6 +306,8 @@ class LessonDataQuerySet(models.QuerySet, WeekQuerySetMixin):
         else:
             week = reference._week
 
+        week = CalendarWeek(week=week, year=reference.lesson.get_year(week))
+
         return self.annotate_week(week).all()[next_index]
 
 
@@ -409,7 +414,7 @@ class SupervisionQuerySet(ValidityRangeRelatedQuerySet, WeekQuerySetMixin):
         """Filter for all supervisions given by a certain teacher."""
         if self.count() > 0:
             if hasattr(self[0], "_week"):
-                week = CalendarWeek(week=self[0]._week)
+                week = CalendarWeek(week=self[0]._week, year=self[0]._year)
             else:
                 week = CalendarWeek.current_week()
 
@@ -517,6 +522,8 @@ class ExtraLessonQuerySet(
         return self.filter(
             week__gte=week_start.week,
             week__lte=week_end.week,
+            year__gte=week_start.year,
+            year__lte=week_end.year,
             period__weekday__gte=start.weekday(),
             period__weekday__lte=end.weekday(),
         )
diff --git a/aleksis/apps/chronos/migrations/0004_substitution_extra_lesson_year.py b/aleksis/apps/chronos/migrations/0004_substitution_extra_lesson_year.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb7383592d167f7d3aa4bf203aaa8cee503d7551
--- /dev/null
+++ b/aleksis/apps/chronos/migrations/0004_substitution_extra_lesson_year.py
@@ -0,0 +1,52 @@
+# Generated by Django 3.0.9 on 2020-08-13 14:06
+
+from django.db import migrations, models
+
+import aleksis.apps.chronos.util.date
+
+
+def migrate_data(apps, schema_editor):
+    LessonSubstitution = apps.get_model("chronos", "LessonSubstitution")
+    ExtraLesson = apps.get_model("chronos", "ExtraLesson")
+
+    db_alias = schema_editor.connection.alias
+
+    for sub in LessonSubstitution.objects.using(db_alias).all():
+        year = sub.lesson_period.lesson.validity.date_start.year
+        if sub.week < int(sub.lesson_period.lesson.validity.date_start.strftime("%V")):
+            year += 1
+        sub.year = year
+        sub.save()
+
+    for extra_lesson in ExtraLesson.objects.using(db_alias).all():
+        year = aleksis.apps.chronos.util.date.get_current_year()
+        extra_lesson.year = year
+        extra_lesson.save()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("core", "0003_drop_image_cropping"),
+        ("chronos", "0003_school_term_validity_fixes"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="extralesson",
+            name="year",
+            field=models.IntegerField(
+                default=aleksis.apps.chronos.util.date.get_current_year,
+                verbose_name="Year",
+            ),
+        ),
+        migrations.AddField(
+            model_name="lessonsubstitution",
+            name="year",
+            field=models.IntegerField(
+                default=aleksis.apps.chronos.util.date.get_current_year,
+                verbose_name="Year",
+            ),
+        ),
+        migrations.RunPython(migrate_data),
+    ]
diff --git a/aleksis/apps/chronos/mixins.py b/aleksis/apps/chronos/mixins.py
index dabaabf6550210088f2d4481b62612f6130ef814..83fc1e289bf29ceedb2bcde045b3e8ca824d3a4e 100644
--- a/aleksis/apps/chronos/mixins.py
+++ b/aleksis/apps/chronos/mixins.py
@@ -1,6 +1,12 @@
+from datetime import date
+from typing import Union
+
 from django.db import models
 from django.utils.translation import gettext as _
 
+from calendarweek import CalendarWeek
+
+from aleksis.apps.chronos.util.date import week_period_to_date
 from aleksis.core.managers import CurrentSiteManagerWithoutMigrations
 from aleksis.core.mixins import ExtensibleModel
 
@@ -25,3 +31,26 @@ class ValidityRangeRelatedExtensibleModel(ExtensibleModel):
 
     class Meta:
         abstract = True
+
+
+class WeekRelatedMixin:
+    @property
+    def date(self) -> date:
+        return week_period_to_date(self.calendar_week, self.lesson_period)
+
+    @property
+    def calendar_week(self) -> CalendarWeek:
+        return CalendarWeek(week=self.week, year=self.year)
+
+
+class WeekAnnotationMixin:
+    @property
+    def week(self) -> Union[CalendarWeek, None]:
+        """Get annotated week as `CalendarWeek`.
+
+        Defaults to `None` if no week is annotated.
+        """
+        if hasattr(self, "_week"):
+            return CalendarWeek(week=self._week, year=self._year)
+        else:
+            return None
diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py
index 336b184b1f2ae4ba6c0a265627c1b82bca16b3c2..31a7fed9592d78e6d88ca66eddc2b3d1233d91c1 100644
--- a/aleksis/apps/chronos/models.py
+++ b/aleksis/apps/chronos/models.py
@@ -35,7 +35,12 @@ from aleksis.apps.chronos.managers import (
     TeacherPropertiesMixin,
     ValidityRangeQuerySet,
 )
-from aleksis.apps.chronos.mixins import ValidityRangeRelatedExtensibleModel
+from aleksis.apps.chronos.mixins import (
+    ValidityRangeRelatedExtensibleModel,
+    WeekAnnotationMixin,
+    WeekRelatedMixin,
+)
+from aleksis.apps.chronos.util.date import get_current_year
 from aleksis.apps.chronos.util.format import format_m2m
 from aleksis.core.managers import CurrentSiteManagerWithoutMigrations
 from aleksis.core.mixins import ExtensibleModel, SchoolTermRelatedExtensibleModel
@@ -135,15 +140,12 @@ class TimePeriod(ValidityRangeRelatedExtensibleModel):
 
         return periods
 
-    def get_date(self, week: Optional[Union[CalendarWeek, int]] = None) -> date:
+    def get_date(self, week: Optional[CalendarWeek] = None) -> date:
         if isinstance(week, CalendarWeek):
             wanted_week = week
         else:
-            year = date.today().year
-            week_number = week or getattr(self, "_week", None) or CalendarWeek().week
-
-            if week_number < SchoolTerm.current.date_start.isocalendar()[1]:
-                year += 1
+            year = getattr(self, "_year", None) or date.today().year
+            week_number = getattr(self, "_week", None) or CalendarWeek().week
 
             wanted_week = CalendarWeek(year=year, week=week_number)
 
@@ -335,12 +337,13 @@ class Lesson(
         verbose_name_plural = _("Lessons")
 
 
-class LessonSubstitution(ExtensibleModel):
+class LessonSubstitution(ExtensibleModel, WeekRelatedMixin):
     objects = LessonSubstitutionManager.from_queryset(LessonSubstitutionQuerySet)()
 
     week = models.IntegerField(
         verbose_name=_("Week"), default=CalendarWeek.current_week
     )
+    year = models.IntegerField(verbose_name=_("Year"), default=get_current_year)
 
     lesson_period = models.ForeignKey(
         "LessonPeriod", models.CASCADE, "substitutions", verbose_name=_("Lesson period")
@@ -379,7 +382,7 @@ class LessonSubstitution(ExtensibleModel):
 
     @property
     def date(self):
-        week = CalendarWeek(week=self.week, year=self.lesson_period.lesson.get_year(self.week))
+        week = CalendarWeek(week=self.week, year=self.year)
         return week[self.lesson_period.period.weekday]
 
     def __str__(self):
@@ -388,7 +391,7 @@ class LessonSubstitution(ExtensibleModel):
     class Meta:
         unique_together = [["lesson_period", "week"]]
         ordering = [
-            "lesson_period__lesson__validity__date_start",
+            "year",
             "week",
             "lesson_period__period__weekday",
             "lesson_period__period__period",
@@ -403,7 +406,7 @@ class LessonSubstitution(ExtensibleModel):
         verbose_name_plural = _("Lesson substitutions")
 
 
-class LessonPeriod(ExtensibleModel):
+class LessonPeriod(ExtensibleModel, WeekAnnotationMixin):
     label_ = "lesson_period"
 
     objects = LessonPeriodManager.from_queryset(LessonPeriodQuerySet)()
@@ -429,14 +432,19 @@ class LessonPeriod(ExtensibleModel):
         verbose_name=_("Room"),
     )
 
-    def get_substitution(self, week: Optional[int] = None) -> LessonSubstitution:
-        wanted_week = week or getattr(self, "_week", None) or CalendarWeek().week
+    def get_substitution(
+        self, week: Optional[CalendarWeek] = None
+    ) -> LessonSubstitution:
+        wanted_week = week or self.week or CalendarWeek()
 
         # We iterate over all substitutions because this can make use of
         # prefetching when this model is loaded from outside, in contrast
         # to .filter()
         for substitution in self.substitutions.all():
-            if substitution.week == wanted_week:
+            if (
+                substitution.week == wanted_week.week
+                and substitution.year == wanted_week.year
+            ):
                 return substitution
         return None
 
@@ -485,17 +493,6 @@ class LessonPeriod(ExtensibleModel):
         """
         return LessonPeriod.objects.filter(lesson=self.lesson).next_lesson(self, -1)
 
-    @property
-    def week(self) -> Union[CalendarWeek, None]:
-        """Get annotated week as `CalendarWeek`.
-
-        Defaults to `None` if no week is annotated.
-        """
-        if hasattr(self, "_week"):
-            return CalendarWeek(week=self._week, year=self._year)
-        else:
-            return None
-
     class Meta:
         ordering = [
             "lesson__validity__date_start",
@@ -797,7 +794,7 @@ class Break(ValidityRangeRelatedExtensibleModel):
         verbose_name_plural = _("Breaks")
 
 
-class Supervision(ValidityRangeRelatedExtensibleModel):
+class Supervision(ValidityRangeRelatedExtensibleModel, WeekAnnotationMixin):
     objects = CurrentSiteManager.from_queryset(SupervisionQuerySet)()
 
     area = models.ForeignKey(
@@ -816,11 +813,16 @@ class Supervision(ValidityRangeRelatedExtensibleModel):
         verbose_name=_("Teacher"),
     )
 
+    def get_year(self, week: int) -> int:
+        year = self.validity.date_start.year
+        if week < int(self.validity.date_start.strftime("%V")):
+            year += 1
+        return year
+
     def get_substitution(
-        self, week: Optional[int] = None
+        self, week: Optional[CalendarWeek] = None
     ) -> Optional[SupervisionSubstitution]:
-        wanted_week = week or getattr(self, "_week", None) or CalendarWeek().week
-        wanted_week = CalendarWeek(week=wanted_week)
+        wanted_week = week or self.week or CalendarWeek()
         # We iterate over all substitutions because this can make use of
         # prefetching when this model is loaded from outside, in contrast
         # to .filter()
@@ -939,7 +941,9 @@ class Event(
         verbose_name_plural = _("Events")
 
 
-class ExtraLesson(SchoolTermRelatedExtensibleModel, GroupPropertiesMixin):
+class ExtraLesson(
+    SchoolTermRelatedExtensibleModel, GroupPropertiesMixin, WeekRelatedMixin
+):
     label_ = "extra_lesson"
 
     objects = CurrentSiteManager.from_queryset(ExtraLessonQuerySet)()
@@ -947,6 +951,7 @@ class ExtraLesson(SchoolTermRelatedExtensibleModel, GroupPropertiesMixin):
     week = models.IntegerField(
         verbose_name=_("Week"), default=CalendarWeek.current_week
     )
+    year = models.IntegerField(verbose_name=_("Year"), default=get_current_year)
     period = models.ForeignKey(
         "TimePeriod",
         models.CASCADE,
diff --git a/aleksis/apps/chronos/tables.py b/aleksis/apps/chronos/tables.py
index 92a966fe11a3d866bdd9d160ebba712ab4e3066d..b24ac8728c07d490e9a23df6dd424b096c08f999 100644
--- a/aleksis/apps/chronos/tables.py
+++ b/aleksis/apps/chronos/tables.py
@@ -14,8 +14,8 @@ def _css_class_from_lesson_state(
     record: Optional[LessonPeriod] = None, table: Optional[LessonsTable] = None
 ) -> str:
     """Return CSS class depending on lesson state."""
-    if record.get_substitution(record._week):
-        if record.get_substitution(record._week).cancelled:
+    if record.get_substitution():
+        if record.get_substitution().cancelled:
             return "success"
         else:
             return "warning"
diff --git a/aleksis/apps/chronos/templatetags/week_helpers.py b/aleksis/apps/chronos/templatetags/week_helpers.py
index c97703175ec5c4f39cf1757b5ee426bddb4e0dbb..2f66d51e449c2a0d46f53700b32b79e4b53fc3fa 100644
--- a/aleksis/apps/chronos/templatetags/week_helpers.py
+++ b/aleksis/apps/chronos/templatetags/week_helpers.py
@@ -22,7 +22,7 @@ def week_end(week: CalendarWeek) -> date:
 @register.filter
 def only_week(qs: QuerySet, week: Optional[CalendarWeek]) -> QuerySet:
     wanted_week = week or CalendarWeek()
-    return qs.filter(week=wanted_week.week)
+    return qs.filter(week=wanted_week.week, year=wanted_week.year)
 
 
 @register.simple_tag
@@ -31,12 +31,12 @@ def weekday_to_date(week: CalendarWeek, weekday: int) -> date:
 
 
 @register.simple_tag
-def period_to_date(week: Union[CalendarWeek, int], period) -> date:
+def period_to_date(week: CalendarWeek, period) -> date:
     return week_period_to_date(week, period)
 
 
 @register.simple_tag
-def period_to_time_start(week: Union[CalendarWeek, int], period) -> date:
+def period_to_time_start(week: CalendarWeek, period) -> date:
     return period.get_datetime_start(week)
 
 
diff --git a/aleksis/apps/chronos/util/build.py b/aleksis/apps/chronos/util/build.py
index 48fc7cddcf4d5e909f49c45525ffc61606e7edde..07b6d68fa06784858680b385e8a10bc8fe97b762 100644
--- a/aleksis/apps/chronos/util/build.py
+++ b/aleksis/apps/chronos/util/build.py
@@ -61,9 +61,9 @@ def build_timetable(
     if is_person:
         extra_lessons = ExtraLesson.objects.on_day(date_ref).filter_from_person(obj)
     else:
-        extra_lessons = ExtraLesson.objects.filter(week=date_ref.week).filter_from_type(
-            type_, obj
-        )
+        extra_lessons = ExtraLesson.objects.filter(
+            week=date_ref.week, year=date_ref.year
+        ).filter_from_type(type_, obj)
 
     # Sort lesson periods in a dict
     extra_lessons_per_period = extra_lessons.group_by_periods(is_person=is_person)
diff --git a/aleksis/apps/chronos/util/chronos_helpers.py b/aleksis/apps/chronos/util/chronos_helpers.py
index 64349e54ee52ba36643d82a973e8baac5124bef2..ae3cc9c934dd4b4e0121f5bac683e850d8ab7a9c 100644
--- a/aleksis/apps/chronos/util/chronos_helpers.py
+++ b/aleksis/apps/chronos/util/chronos_helpers.py
@@ -32,5 +32,5 @@ def get_substitution_by_id(request: HttpRequest, id_: int, week: int):
     wanted_week = lesson_period.lesson.get_calendar_week(week)
 
     return LessonSubstitution.objects.filter(
-        week=wanted_week.week, lesson_period=lesson_period
+        week=wanted_week.week, year=wanted_week.year, lesson_period=lesson_period
     ).first()
diff --git a/aleksis/apps/chronos/util/date.py b/aleksis/apps/chronos/util/date.py
index e31775ef74860c2276bfaf39d758c283691e487a..159f8e4cbfa08df8d5bf286b141e482591bfc658 100644
--- a/aleksis/apps/chronos/util/date.py
+++ b/aleksis/apps/chronos/util/date.py
@@ -1,6 +1,8 @@
 from datetime import date
 from typing import List, Tuple, Union
 
+from django.utils import timezone
+
 from calendarweek import CalendarWeek
 
 
@@ -14,7 +16,7 @@ def week_weekday_to_date(week: CalendarWeek, weekday: int) -> date:
     return week[weekday]
 
 
-def week_period_to_date(week: Union[CalendarWeek, int], period) -> date:
+def week_period_to_date(week: CalendarWeek, period) -> date:
     """Return the date of a lesson period in a given week."""
     return period.get_date(week)
 
@@ -31,3 +33,9 @@ def get_weeks_for_year(year: int) -> List[CalendarWeek]:
         current_week += 1
 
     return weeks
+
+
+def get_current_year() -> int:
+    """Get current year."""
+
+    return timezone.now().year