django-celery-beat icon indicating copy to clipboard operation
django-celery-beat copied to clipboard

Time issue

Open xiaobao0505 opened this issue 9 months ago • 10 comments

I think there's something wrong with the time of model.date_changed in line 96 of schedulers.py. https://github.com/celery/django-celery-beat/blob/f1f239c5900bd2b1d24c9f368da7b344627ec500/django_celery_beat/schedulers.py#L96

Maybe it needs to be converted according to the configuration of DJANGO_CELERY_BEAT_TZ_AWARE?

xiaobao0505 avatar Jul 14 '25 08:07 xiaobao0505

can you help to reproduce the problem with a failing test case as PR?

auvipy avatar Jul 14 '25 08:07 auvipy

I'm a beginner and I'm not quite sure if the issue is with model.date_changed. Here are my reproduction steps:

  • settings:
    • USE_TZ = False
    • DJANGO_CELERY_BEAT_TZ_AWARE = False
ModelEntry.is_due 

last_run_at_in_tz = maybe_make_aware(self.last_run_at).astimezone(tz)
info(f"self.model.date_changed={self.model.date_changed} last_run_at={self.last_run_at} last_run_at_in_tz={last_run_at_in_tz}") # add this
return self.schedule.is_due(last_run_at_in_tz)
  • Step 1:

    • Start the beat using v2.7.0
    • log: [2025-07-15 10:18:08,193: INFO/MainProcess] self.model.date_changed=2025-07-15 10:07:30.892475 last_run_at=2025-07-15 02:17:58.134552 last_run_at_in_tz=2025-07-15 10:17:58.134552+08:00
    • The task is triggered normally.
  • Step 2:

    • The task needs to be re-enabled. Otherwise, the task can run normally.
  • Step 3:

    • Start the beat using v2.8.1
    • log: [2025-07-15 10:19:17,501: INFO/MainProcess] self.model.date_changed=2025-07-15 10:19:13.690588 last_run_at=2025-07-15 10:19:13.690588 last_run_at_in_tz=2025-07-15 18:19:13.690588+08:00
    • The task will no longer be triggered.

According to the logs, the format of last_run_at differs between v2.7.0 and v2.8.1. So, I think there should be a minor issue here.

xiaobao0505 avatar Jul 15 '25 02:07 xiaobao0505

did you try from main branch? there are some new commits after 2.8.1 release https://github.com/celery/django-celery-beat/commits/main/

auvipy avatar Jul 15 '25 04:07 auvipy

I tried, but it didn't work.

xiaobao0505 avatar Jul 15 '25 05:07 xiaobao0505

I think it's still caused by the fact that last_run_at uses model.date_changed.

I modified the code from model.last_run_at = model.date_changed or self._default_now() to model.last_run_at = self._default_now(), and the task was triggered normally. It may still be necessary to convert model.date_changed.

xiaobao0505 avatar Jul 15 '25 05:07 xiaobao0505

you can fork this repo, then create a new branch and share a PR on what change you made.

auvipy avatar Jul 15 '25 06:07 auvipy

I also ran into this problem and as mentioned in previous comments, it seems to be related to this line:

model.last_run_at = model.date_changed or self._default_now()

The problem in my environment is that _default_now() is in UTC and model.date_changed is a naive time in the server's timezone. Passing model.date_changed to maybe_make_aware() will result in the actual time +/- the tz offset. In my case it's +2 right now, which ends up being in the future.

The following change could fix it, while maintaining the intended behaviour of date_changed. Unfortunately I'm not very experienced with timezones and Celery, so maybe this is not the right way. I also don't know how to run the tests and create a test case for it.

--- a/django_celery_beat/schedulers.py
+++ b/django_celery_beat/schedulers.py
@@ -93,7 +93,10 @@ class ModelEntry(ScheduleEntry):
         self.model = model
 
         if not model.last_run_at:
-            model.last_run_at = model.date_changed or self._default_now()
+            date_changed = model.date_changed
+            if date_changed and not getattr(settings, 'DJANGO_CELERY_BEAT_TZ_AWARE', True):
+                date_changed = date_changed.replace(tzinfo=self.app.timezone)
+            model.last_run_at = date_changed or self._default_now()
             # if last_run_at is not set and
             # model.start_time last_run_at should be in way past.
             # This will trigger the job to run at start_time

adi1 avatar Jul 21 '25 22:07 adi1

same problem

akkuman avatar Nov 13 '25 08:11 akkuman

settings.py

TIME_ZONE = "Asia/Shanghai"
USE_TZ = False
DJANGO_CELERY_BEAT_TZ_AWARE = False
CELERY_TIMEZONE = TIME_ZONE

It will lead to the following result:

my current time: 2025-11-13 16:34:13+08:00

model.date_changed: 2025-11-13 16:34:14.117205
self.last_run_at: 2025-11-13 16:34:13.977566
maybe_make_aware(self.last_run_at): 2025-11-13 16:34:13.977566+00:00
last_run_at_in_tz: 2025-11-14 00:34:13.977566+08:00
        if not model.last_run_at:
            model.last_run_at = model.date_changed or self._default_now()

        self.last_run_at = model.last_run_at

tz = self.app.timezone

last_run_at_in_tz = maybe_make_aware(self.last_run_at).astimezone(tz)

akkuman avatar Nov 13 '25 08:11 akkuman

This issue was introduced at https://github.com/celery/django-celery-beat/pull/717, and downgrading to version 2.7.0 resolves the problem.

akkuman avatar Nov 13 '25 09:11 akkuman