dbader / schedule

Python job scheduling for humans.
https://schedule.readthedocs.io/
MIT License
11.86k stars 966 forks source link

daylight saving bug #608

Closed bellt closed 6 months ago

bellt commented 1 year ago

This is a re-open of #598 I schedule a daily job to run at a particular time in UTC. Most of the year the job runs at the correct local time corresponding to that time in UTC. However for approximately three weeks of the year an incorrect offset from UTC gets applied and the job runs at the wrong time. The weeks when the job runs at the incorrect time are: a. One week after daylight saving comes into effect in my local timezone b. Roughly two weeks before daylight saving ends in my local timezone.

Here are some tests to demonstrate this. The first two tests pass

with mock_datetime(2023, 9, 18, 10, 00, 0, TZ_AUCKLAND):
    # Testing timezone conversion the week before daylight saving comes into effect
    # Current time: Monday 18 September 10:00 NZST
    # Current time UTC: Sunday 17 September 22:00
    # Expected next run in NZST: 2023-09-18 11:00:00
    schedule.clear()
    next = schedule.every().day.at("23:00", "UTC").do(mock_job).next_run
    assert next.day == 18
    assert next.hour == 11
    assert next.minute == 0

with mock_datetime(2024, 4, 8, 10, 00, 0, TZ_AUCKLAND):
    # Testing timezone conversion the week after daylight saving ends
    # Current time: Monday 8 April 10:00 NZST
    # Current time UTC: Sunday 7 April 22:00
    # Expected next run in NZDT: 2023-04-08 11:00:00
    schedule.clear()
    next = schedule.every().day.at("23:00", "UTC").do(mock_job).next_run
    assert next.day == 8
    assert next.hour == 11
    assert next.minute == 0

And these next two tests fail

with mock_datetime(2023, 9, 25, 10, 00, 0, TZ_AUCKLAND):
    # Testing timezone conversion during the week after daylight saving comes into effect
    # Current time: Monday 25 September 10:00 NZDT
    # Current time UTC: Sunday 24 September 21:00
    # Expected next run in NZDT: 2023-09-25 12:00:00
    schedule.clear()
    next = schedule.every().day.at("23:00", "UTC").do(mock_job).next_run
    assert next.day == 25
    assert next.hour == 12 #FAIL
    assert next.minute == 0

with mock_datetime(2024, 4, 1, 10, 00, 0, TZ_AUCKLAND):
    # Testing timezone conversion during the week before daylight saving ends
    # Current time: Monday 1 April 10:00 NZDT
    # Current time UTC: Sunday 31 March 21:00
    # Expected next run in NZDT: 2024-04-01 12:00:00
    schedule.clear()
    next = schedule.every().day.at("23:00", "UTC").do(mock_job).next_run
    assert next.day == 1
    assert next.hour == 12 #FAIL
    assert next.minute == 0

If you'd like me to do a PR to include these tests then let me know.

bellt commented 11 months ago

It looked like the call to astimezone() on line 794 of __init__.py was returning the incorrect time in the failed tests. I was able to get all four of the above tests to pass by explicitly giving that method my local timezone like this

import pytz
# self.next_run = self.next_run.astimezone().replace(tzinfo=None)
self.next_run = self.next_run.astimezone(pytz.timezone('Pacific/Auckland')).replace(tzinfo=None)

However I haven't been able to find a clean way get the users local timezone and pass that in, so I can't run the other tests. Hence I'm not sure if this is the root source of the issue or if it is a red herring.

SijmenHuizenga commented 7 months ago

I know it has been ages since you submitted this... I'm now trying to solve the timezone bugs and your unit tests are tremendously helpful. Thank you.

After a lot of debugging i found that the definition of Auckland in the unit tests are wrong... Somehow it was set to change winter- to summertime in October instead of september :sob:

-- TZ_AUCKLAND = "NZST-12NZDT-13,M10.1.0/02:00:00,M3.3.0/03:00:00"
++ TZ_AUCKLAND = "NZST-12NZDT,M9.5.0,M4.1.0/3"

That doesn't resolve the cross-timezone scheduling bug, but it's a start to figure out what is going on.

bellt commented 7 months ago

Oh whoops!

I can confirm that when we changed from daylight saving time to standard time earlier in April, my UTC-scheduled jobs started running an hour earlier one week before daylight saving ended.