diff --git a/airflow-core/src/airflow/timetables/_cron.py b/airflow-core/src/airflow/timetables/_cron.py index ed0b3c76bb786..245cc3fbc3953 100644 --- a/airflow-core/src/airflow/timetables/_cron.py +++ b/airflow-core/src/airflow/timetables/_cron.py @@ -131,7 +131,7 @@ def __eq__(self, other: object) -> bool: return self._expression == other._expression and self._timezone == other._timezone def __hash__(self): - return hash((self._expression, self._timezone)) + return hash((self._expression, str(self._timezone))) @property def summary(self) -> str: diff --git a/airflow-core/tests/unit/timetables/test_trigger_timetable.py b/airflow-core/tests/unit/timetables/test_trigger_timetable.py index f4718f8d04bb9..02a58323e14ea 100644 --- a/airflow-core/tests/unit/timetables/test_trigger_timetable.py +++ b/airflow-core/tests/unit/timetables/test_trigger_timetable.py @@ -1196,3 +1196,10 @@ def test_run_immediately_does_not_pick_future_run(get_info: typing.Callable[[], assert info is not None assert info.run_after <= pendulum.DateTime(2024, 11, 3, 5, 30, tzinfo=utc) assert info.run_after == pendulum.DateTime(2024, 11, 2, 5, tzinfo=utc) + + +def test_cron_timetables_are_hashable(): + timetable = CronTriggerTimetable("0 0 * * *", timezone="UTC") + same = CronTriggerTimetable("0 0 * * *", timezone="UTC") + assert timetable == same + assert hash(timetable) == hash(same)