-
Notifications
You must be signed in to change notification settings - Fork 430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Issue #604: Celery Beat Crashing at the End of Daylight Savings #605
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
beside it will be very useful to have proper unit tests for this change
value = timezone.make_aware(value, timezone.get_default_timezone()) | ||
tm_isdst = time.localtime().tm_isdst | ||
|
||
# tm_isdst may return -1 if it cannot be determined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure about this part, can any other way we can check it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I referenced these posts 1 (top answer only) and 2. There are other suggested solutions online. I can summarize the problems with these solutions.
Use of localize
The root cause was inside localize
. Using the localize
to fix the problem with localize
sounds counterintuitive. For instance, link 1 mentions the following in the final solution:
Once you've **localized** a datetime in fact you can simply check it's .dst() method to see if it's zero.
time.daylight
This solution might work, but the underlying code is just to do a similar check to what I do here. This code is cleaner, and I can change it if you would like.
Calculations
One way is to calculate the timezone based on the current time and a previous time, but I think that this solution is long and too ad-hoc when we can achieve this with a few lines of code.
Conclusion
I think that time.daylight
or time.localtime
+tm_isdst
is the best way to go for this. If you believe in the other solutions, please let me know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to check to give you proper answer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. Let me know when you've checked.
I added the unit tests. |
bc6f65c
to
b75637f
Compare
…g in None all the time
for more information, see https://pre-commit.ci
Now that tests are passing on Django v5 this all becomes more difficult because:
https://docs.djangoproject.com/en/5.0/releases/5.0/#features-removed-in-5-0 |
@polarmt Please rebase to resolve the git conflicts. |
@polarmt Tests are ~50/50 pass/fail and in Django v5 the default value of the USE_TZ setting is changed from False to True. Now that we have release v2.6.0 that supports Django v5, how should we proceed with this effort? |
Dependencies
celery/celery#7901
Note: The two PRs are codependent and need to be merged together.
Overview
This is my suggested fix for #604. The following issue assumes
America/Los_Angeles
.Testing
All testing was done on the following versions:
django: 3.2.15
celery: 5.2.7
django-celery-beat: 2.4.0
USE_TZ
is not set.Reproducing the Issue
In the Django shell (
python manage.py shell
):Without the changes in this PR, this should throw an
AmbiguousTimeError
.Behavior of
remaining
During Transition (Without celery/celery#7901)We can run the following script to test how the Celery beat will calculate the time remaining during the transition:
now
here would be1:34 AM PST
or1:34:00-8:00
whilestart
would be1:15 AM PDT
or1:15:00-7:00
. The correct return value oftime.remaining(...)
here would be 1 minute since 1 hour 20 minutes fromstart
would be1:35:00-8:00
. The problem is thattime.remaining(...)
returns 1 hour 1 minute becausetime.remaining
will convert1:15:00-7:00
to1:15-8:00
.Behavior of
remaining
During Transition (With celery/celery#7901)We can use the same script as the above subsection after making the changes in celery/celery#7901. This will cause
time.remaining(...)
to return 1 minute instead of 1 hour 1 minute, which is the correct value.