diff --git a/program/filters.py b/program/filters.py
index d32554aaee756fa1afb3d4cf8189a36ecc5cdfa9..d4b9728cf7ebe98938e68a776fa74f9cc655f18a 100644
--- a/program/filters.py
+++ b/program/filters.py
@@ -170,6 +170,19 @@ class ScheduleFilterSet(filters.FilterSet):
         field_name="show",
         help_text="Return only schedules that belong to the specified show(s).",
     )
+    exclude_inactive = filters.BooleanFilter(
+        method="filter_exclude_inactive",
+        help_text="Excludes all schedules that don’t have timeslots in the future.",
+    )
+
+    def filter_exclude_inactive(self, queryset: QuerySet, name: str, value: bool):
+        if not value:
+            return queryset
+        return queryset.filter(
+            Exists(
+                models.TimeSlot.objects.filter(schedule=OuterRef("pk"), end__gte=timezone.now())
+            )
+        )
 
 
 class TimeSlotFilterSet(filters.FilterSet):
@@ -184,29 +197,30 @@ class TimeSlotFilterSet(filters.FilterSet):
             "If specified without a datetime value the current date and time is assumed."
         ),
     )
-    # The start/end filters will always be applied even if no query parameter has been set.
-    # This is because we enforce a value in the clean_start and clean_end methods
-    # of the filterset form.
-    start = filters.DateFilter(
-        method="filter_start",
-        help_text=(
-            "Only returns timeslots after that start on or after the specified date. "
-            "By default, this is set to the current date."
-        ),
+    starts_before = filters.DateTimeFilter(
+        field_name="start",
+        lookup_expr="lt",
+        help_text="Only returns timeslots that start before the specified datetime.",
     )
-    end = filters.DateFilter(
-        method="filter_end",
-        help_text=(
-            "Only returns timeslots that end on or before the specified date. "
-            "By default, this is set to value of the start filter + 60 days."
-        ),
+    starts_after = filters.DateTimeFilter(
+        field_name="start",
+        lookup_expr="gte",
+        help_text="Only returns timeslots that start at or after the specified datetime.",
+    )
+    ends_before = filters.DateTimeFilter(
+        field_name="end",
+        lookup_expr="lt",
+        help_text="Only returns timeslots that end before the specified datetime.",
+    )
+    ends_after = filters.DateTimeFilter(
+        field_name="end",
+        lookup_expr="gte",
+        help_text="Only returns timeslots that end at or after the specified datetime.",
     )
-
     schedule_ids = IntegerInFilter(
         field_name="schedule",
         help_text="Return only timeslots that belong to the specified schedule(s).",
     )
-
     show_ids = IntegerInFilter(
         field_name="schedule__show",
         help_text="Return only timeslots that belong to the specified show(s).",
@@ -226,14 +240,6 @@ class TimeSlotFilterSet(filters.FilterSet):
         relevant_timeslot_ids = list(nearest_timeslots_in_future) + list(nearest_timeslots_in_past)
         return queryset.filter(id__in=relevant_timeslot_ids)
 
-    def filter_start(self, queryset: QuerySet, name: str, value: datetime.date):
-        start = timezone.make_aware(datetime.datetime.combine(value, datetime.time.min))
-        return queryset.filter(start__gte=start)
-
-    def filter_end(self, queryset: QuerySet, name: str, value: datetime.date):
-        end = timezone.make_aware(datetime.datetime.combine(value, datetime.time.max))
-        return queryset.filter(end__lte=end)
-
     def filter_queryset(self, queryset):
         queryset = super().filter_queryset(queryset)
         # This is for backwards compatibility as the surrounding-filter was formerly implemented
@@ -242,26 +248,6 @@ class TimeSlotFilterSet(filters.FilterSet):
             queryset = self.filter_surrounding(queryset, "surrounding", timezone.now())
         return queryset
 
-    def get_form_class(self):
-        form_cls = super().get_form_class()
-
-        class TimeSlotFilterSetFormWithDefaults(form_cls):
-            def clean_start(self):
-                start = self.cleaned_data.get("start", None)
-                return start or timezone.now().date()
-
-            def clean_end(self):
-                end = self.cleaned_data.get("end", None)
-                return end or self.cleaned_data["start"] + datetime.timedelta(days=60)
-
-        # We only want defaults to apply in the context of the list action.
-        # When accessing individual timeslots we don’t want the queryset to be restricted
-        # to the default range of 60 days as get_object would yield a 404 otherwise.
-        if self.request.parser_context["view"].action == "list":
-            return TimeSlotFilterSetFormWithDefaults
-        else:
-            return form_cls
-
     class Meta:
         model = models.TimeSlot
         fields = [
diff --git a/program/serializers.py b/program/serializers.py
index 1c407c918bd0dd380aad8871df01c2c15fd4ef70..b556b32da55e25c6975caf57f45922ef36e23486 100644
--- a/program/serializers.py
+++ b/program/serializers.py
@@ -681,7 +681,12 @@ class RRuleSerializer(serializers.ModelSerializer):
     class Meta:
         model = RRule
         fields = (
+            "by_set_pos",
+            "by_weekdays",
+            "count",
+            "freq",
             "id",
+            "interval",
             "name",
         )
         read_only_fields = fields