Repository: celery/django-celery-beat

Issue:
REGEXP_LIKE incompatible with SQL Server
There's a single use of __regex filter introduced in https://github.com/celery/django-celery-beat/commit/87c059706506994f677e82426c80028ad9894af1 which breaks celery-beat when using SQL Server. 
* #879

https://learn.microsoft.com/en-us/sql/t-sql/functions/regexp-like-transact-sql?view=azuresqldb-current states that COMPATIBILITY_LEVEL 170 is required for that function to work. That level is not available (at least not for most versions)!

![Image](https://github.com/user-attachments/assets/6270bad4-1829-4ced-9114-98844e674cf1)


Discussion:
@alirafiei75
I will check it now.



Patch:
diff --git a/django_celery_beat/schedulers.py b/django_celery_beat/schedulers.py
index dce0a077..99d98f8c 100644
--- a/django_celery_beat/schedulers.py
+++ b/django_celery_beat/schedulers.py
@@ -314,13 +314,10 @@ def _get_crontab_exclude_query(self):
         ]
         hours_to_include += [4]  # celery's default cleanup task
 
-        # Regex pattern to match only numbers
-        # This ensures we only process numeric hour values
-        numeric_hour_pattern = r'^\d+$'
-
         # Get all tasks with a simple numeric hour value
+        valid_numeric_hours = self._get_valid_hour_formats()
         numeric_hour_tasks = CrontabSchedule.objects.filter(
-            hour__regex=numeric_hour_pattern
+            hour__in=valid_numeric_hours
         )
 
         # Annotate these tasks with their server-hour equivalent
@@ -359,6 +356,15 @@ def _get_crontab_exclude_query(self):
 
         return exclude_query
 
+    def _get_valid_hour_formats(self):
+        """
+        Return a list of all valid hour values (0-23).
+        Both zero-padded ("00"–"09") and non-padded ("0"–"23")
+        """
+        return [str(hour) for hour in range(24)] + [
+            f"{hour:02d}" for hour in range(10)
+        ]
+
     def _get_unique_timezone_names(self):
         """Get a list of all unique timezone names used in CrontabSchedule"""
         return CrontabSchedule.objects.values_list(


Test patch:
diff --git a/t/unit/test_schedulers.py b/t/unit/test_schedulers.py
index 6ccc88bf..3a9d757d 100644
--- a/t/unit/test_schedulers.py
+++ b/t/unit/test_schedulers.py
@@ -1212,6 +1212,41 @@ def test_crontab_timezone_conversion_with_negative_offset_and_dst(
             "Paris outside window task should be excluded"
         )
 
+    def test_scheduler_valid_hours(self):
+        """Test the _get_valid_hour_formats method."""
+        # Create an instance of DatabaseScheduler
+        s = self.Scheduler(app=self.app)
+
+        # Get the valid hours list
+        valid_hours = s._get_valid_hour_formats()
+
+        # Basic validations
+        # 24 regular hours + 10 zero-padded hours
+        assert len(valid_hours) == 34
+
+        # Check both regular and zero-padded formats are present
+        assert "0" in valid_hours
+        assert "00" in valid_hours
+        assert "9" in valid_hours
+        assert "09" in valid_hours
+        assert "23" in valid_hours
+
+        # Verify all hours 0-23 are included
+        for hour in range(24):
+            assert str(hour) in valid_hours
+
+        # Verify zero-padded hours 00-09 are included
+        for hour in range(10):
+            assert f"{hour:02d}" in valid_hours
+
+        # Check for duplicates (set should have same length as list)
+        assert len(set(valid_hours)) == len(valid_hours)
+
+        # Verify all entries are valid hour representations
+        for hour_str in valid_hours:
+            hour_value = int(hour_str)
+            assert 0 <= hour_value <= 23
+
 
 @pytest.mark.django_db
 class test_models(SchedulerCase):