Skip to content

Commit 6ac8073

Browse files
committed
Fix timezone support for run_after DATE_MAX
1 parent 945dfb3 commit 6ac8073

File tree

5 files changed

+63
-8
lines changed

5 files changed

+63
-8
lines changed

django_tasks/backends/base.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import Any, TypeVar
55

66
from asgiref.sync import sync_to_async
7+
from django.conf import settings
78
from django.core.checks import messages
89
from django.db import connections
910
from django.utils import timezone
@@ -83,7 +84,11 @@ def validate_task(self, task: Task) -> None:
8384
if not self.supports_defer and task.run_after is not None:
8485
raise InvalidTaskError("Backend does not support run_after")
8586

86-
if task.run_after is not None and not timezone.is_aware(task.run_after):
87+
if (
88+
settings.USE_TZ
89+
and task.run_after is not None
90+
and not timezone.is_aware(task.run_after)
91+
):
8792
raise InvalidTaskError("run_after must be an aware datetime")
8893

8994
if self.queues and task.queue_name not in self.queues:

django_tasks/backends/database/migrations/0016_remove_dbtaskresult_django_task_new_ordering_idx_and_more.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import datetime
44

5+
from django.conf import settings
56
from django.db import migrations, models
67
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
78
from django.db.migrations.state import StateApps
@@ -55,7 +56,12 @@ class Migration(migrations.Migration):
5556
name="run_after",
5657
field=models.DateTimeField(
5758
default=datetime.datetime(
58-
9999, 1, 1, 0, 0, tzinfo=datetime.timezone.utc
59+
9999,
60+
1,
61+
1,
62+
0,
63+
0,
64+
tzinfo=datetime.timezone.utc if settings.USE_TZ else None,
5965
),
6066
verbose_name="run after",
6167
),

django_tasks/backends/database/models.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar
55

66
import django
7+
from django.conf import settings
78
from django.core.exceptions import SuspiciousOperation
89
from django.db import models
910
from django.db.models import F, Q
@@ -48,7 +49,10 @@ def __class_getitem__(cls, _):
4849
return cls
4950

5051

51-
DATE_MAX = datetime.datetime(9999, 1, 1, tzinfo=datetime.timezone.utc)
52+
def get_date_max() -> datetime.datetime:
53+
return datetime.datetime(
54+
9999, 1, 1, tzinfo=datetime.timezone.utc if settings.USE_TZ else None
55+
)
5256

5357

5458
class DBTaskResultQuerySet(models.QuerySet):
@@ -58,7 +62,9 @@ def ready(self) -> "DBTaskResultQuerySet":
5862
"""
5963
return self.filter(
6064
status=ResultStatus.READY,
61-
).filter(models.Q(run_after=DATE_MAX) | models.Q(run_after__lte=timezone.now()))
65+
).filter(
66+
models.Q(run_after=get_date_max()) | models.Q(run_after__lte=timezone.now())
67+
)
6268

6369
def succeeded(self) -> "DBTaskResultQuerySet":
6470
return self.filter(status=ResultStatus.SUCCEEDED)
@@ -157,7 +163,7 @@ def task(self) -> Task[P, T]:
157163
return task.using(
158164
priority=self.priority,
159165
queue_name=self.queue_name,
160-
run_after=None if self.run_after == DATE_MAX else self.run_after,
166+
run_after=None if self.run_after == get_date_max() else self.run_after,
161167
backend=self.backend_name,
162168
)
163169

django_tasks/backends/database/signal_handlers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
from django.db.models.signals import pre_save
44
from django.dispatch import receiver
55

6-
from .models import DATE_MAX, DBTaskResult
6+
from .models import DBTaskResult, get_date_max
77

88

99
@receiver(pre_save, sender=DBTaskResult)
1010
def set_run_after(sender: Any, instance: DBTaskResult, **kwargs: Any) -> None:
1111
if instance.run_after is None:
12-
instance.run_after = DATE_MAX
12+
instance.run_after = get_date_max()

tests/tests/test_database_backend.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
import sys
88
import time
99
import uuid
10+
import warnings
1011
from collections import Counter
1112
from contextlib import redirect_stderr
12-
from datetime import timedelta
13+
from datetime import datetime, timedelta
1314
from functools import partial
1415
from io import StringIO
1516
from typing import Any, List, Optional, Sequence, Union, cast
@@ -436,6 +437,43 @@ def test_index_scan_for_ready(self) -> None:
436437
else:
437438
self.fail("Unknown database engine")
438439

440+
def test_run_after_tz(self) -> None:
441+
for use_tz in [True, False]:
442+
with self.subTest(use_tz=use_tz):
443+
with override_settings(USE_TZ=use_tz):
444+
result = test_tasks.noop_task.enqueue()
445+
self.assertIsNone(
446+
DBTaskResult.objects.get(id=result.id).task.run_after
447+
)
448+
449+
def test_run_after_null_0016_migration(self) -> None:
450+
from datetime import timezone
451+
452+
for use_tz in [True, False]:
453+
with self.subTest(use_tz=use_tz):
454+
with override_settings(USE_TZ=use_tz):
455+
result = test_tasks.noop_task.enqueue()
456+
457+
db_result = DBTaskResult.objects.get(id=result.id)
458+
459+
# Literal taken from migration
460+
db_result.run_after = datetime(
461+
9999,
462+
1,
463+
1,
464+
tzinfo=timezone.utc if use_tz else None,
465+
)
466+
467+
with warnings.catch_warnings():
468+
warnings.filterwarnings(
469+
"ignore", module="django.db", category=RuntimeWarning
470+
)
471+
db_result.save()
472+
473+
self.assertIsNone(
474+
DBTaskResult.objects.get(id=result.id).task.run_after
475+
)
476+
439477

440478
@override_settings(
441479
TASKS={

0 commit comments

Comments
 (0)