From e7406b99d976fc5ae4fe6eb5a1aeeb05053154ec Mon Sep 17 00:00:00 2001 From: Badri467 Date: Sat, 8 Aug 2026 00:51:05 +0530 Subject: [PATCH 1/2] fix(filters): order date range by parsed datetime, not string comparison Fixes #9567 --- .../utils/test_legacy_filter_converter.py | 35 +++++++++++++++++++ apps/api/plane/utils/filters/converters.py | 9 +++-- 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 apps/api/plane/tests/unit/utils/test_legacy_filter_converter.py diff --git a/apps/api/plane/tests/unit/utils/test_legacy_filter_converter.py b/apps/api/plane/tests/unit/utils/test_legacy_filter_converter.py new file mode 100644 index 00000000000..45b452f3059 --- /dev/null +++ b/apps/api/plane/tests/unit/utils/test_legacy_filter_converter.py @@ -0,0 +1,35 @@ +# Copyright (c) 2023-present Plane Software, Inc. and contributors +# SPDX-License-Identifier: AGPL-3.0-only +# See the LICENSE file for details. + +import pytest + +from plane.utils.filters.converters import LegacyToRichFiltersConverter + + +@pytest.mark.unit +class TestLegacyToRichFiltersConverterDateOrdering: + """ + _convert_date_value must order date range bounds chronologically, + not lexicographically. See: https://github.com/makeplane/plane/issues/9567 + """ + + def setup_method(self): + self.converter = LegacyToRichFiltersConverter() + + def test_non_iso_date_range_is_ordered_chronologically(self): + # '9/1/2023' (Sep 1) < '10/1/2023' (Oct 1) chronologically, but + # '10/1/2023' < '9/1/2023' lexicographically ('1' < '9'). + # The old code used string min/max and produced a reversed range. + result = self.converter._convert_date_value( + "target_date", + ["9/1/2023;after", "10/1/2023;before"], + ) + assert result["target_date__range"] == "2023-09-01,2023-10-01" + + def test_iso_date_range_still_works(self): + result = self.converter._convert_date_value( + "target_date", + ["2023-09-01;after", "2023-12-31;before"], + ) + assert result["target_date__range"] == "2023-09-01,2023-12-31" \ No newline at end of file diff --git a/apps/api/plane/utils/filters/converters.py b/apps/api/plane/utils/filters/converters.py index 4d37c2b0b17..1680b44a125 100644 --- a/apps/api/plane/utils/filters/converters.py +++ b/apps/api/plane/utils/filters/converters.py @@ -306,8 +306,13 @@ def _convert_date_value(self, field_name: str, values: List[str], strict: bool = result = {} if len(after_dates) == 1 and len(before_dates) == 1 and len(exact_dates) == 0: # Simple range: one after and one before - start_date = min(after_dates[0], before_dates[0]) - end_date = max(after_dates[0], before_dates[0]) + # Parse to datetime objects so comparison is chronological, not lexicographic. + # String min/max only works for zero-padded ISO YYYY-MM-DD; other formats + # accepted by _validate_date (e.g. M/D/YYYY) would produce a reversed range. + after_dt = dateutil_parse(after_dates[0]) + before_dt = dateutil_parse(before_dates[0]) + start_date = min(after_dt, before_dt).strftime("%Y-%m-%d") + end_date = max(after_dt, before_dt).strftime("%Y-%m-%d") self._add_rich_filter(result, field_name, "range", [start_date, end_date]) elif len(exact_dates) == 1 and len(after_dates) == 0 and len(before_dates) == 0: # Single exact date From 30885497910789b4a812da04bee3e2a714f794fe Mon Sep 17 00:00:00 2001 From: Badri467 Date: Sat, 8 Aug 2026 01:41:42 +0530 Subject: [PATCH 2/2] fix: use .date() to avoid naive/aware datetime TypeError --- apps/api/plane/utils/filters/converters.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/api/plane/utils/filters/converters.py b/apps/api/plane/utils/filters/converters.py index 1680b44a125..567acb0b628 100644 --- a/apps/api/plane/utils/filters/converters.py +++ b/apps/api/plane/utils/filters/converters.py @@ -309,10 +309,10 @@ def _convert_date_value(self, field_name: str, values: List[str], strict: bool = # Parse to datetime objects so comparison is chronological, not lexicographic. # String min/max only works for zero-padded ISO YYYY-MM-DD; other formats # accepted by _validate_date (e.g. M/D/YYYY) would produce a reversed range. - after_dt = dateutil_parse(after_dates[0]) - before_dt = dateutil_parse(before_dates[0]) - start_date = min(after_dt, before_dt).strftime("%Y-%m-%d") - end_date = max(after_dt, before_dt).strftime("%Y-%m-%d") + after_date = dateutil_parse(after_dates[0]).date() + before_date = dateutil_parse(before_dates[0]).date() + start_date = min(after_date, before_date).isoformat() + end_date = max(after_date, before_date).isoformat() self._add_rich_filter(result, field_name, "range", [start_date, end_date]) elif len(exact_dates) == 1 and len(after_dates) == 0 and len(before_dates) == 0: # Single exact date