apscheduler: Standard tzinfo is not enough for apscheduler
Expected Behavior
apscheduler
should work with next_run_time
specified with standard tzinfo
.
Current Behavior
apscheduler
crashes.
Steps to Reproduce
from datetime import datetime, timedelta
from dateutil.tz import tzutc
next_run_time = datetime.now(tzutc()) + timedelta(seconds=30)
scheduler.add_job(
lambda: print("Hello"), trigger=IntervalTrigger(minutes=1), next_run_time=next_run_time
)
Context (Environment)
apscheduler
works only with pytz
objects, the problematic call is there : https://github.com/agronholm/apscheduler/blob/18b50d9ee9ff14e816b557e34d7d3abc861d57e5/apscheduler/triggers/interval.py#L66
If next_fire_time
is not stamped with a pytz.tzinfo.BaseTzInfo
, it crashes.
self.timezone
is a pytz
object, normalize()
is a pytz
function which expects a pytz.tzinfo.BaseTzInfo
as argument (see https://github.com/stub42/pytz/blob/master/src/pytz/tzinfo.py#L66) with an internal (and absent from the standard tzinfo
) _utcoffset
there : https://github.com/stub42/pytz/blob/master/src/pytz/tzinfo.py#L252.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 25 (13 by maintainers)
v4.0.0a1 accepts a wider range of tzinfo objects. If you still have trouble, open a new issue against v4.0.0a1.
It already does, in
master
.Yes, it is, and progress is sporadic since I am also working on several other projects. That said, the triggers part is more or less done, and it supports a far wider variety of tzinfo objects. Date-time arithmetic is done by converting the starting datetime to a timestamp, adding the desired number of seconds and then converting it back. That is the only way to get the semantics we want.
Dateutil is a third party library, just as pytz is. Neither is considered any more “standard” than the other.
I tried playing around with dateutil timezones. It should be noted that, unlike pytz which comes with its own copy of the Olsen database, dateutil relies on system-provided timezone data and thus will not work the same way on Windows.
The following snippet should go some way towards demonstrating why I stick with pytz and will not bother with dateutil:
As you can see, the official way to get the timezone name is not helpful.
We get the correct UTC offset here. Let’s try moving it 2 minutes forward which, taking the DST shift into account, should arrive at
2020-03-29T04:01:00+03:00
:Nonexistent time, but doesn’t matter if we normalize, let’s try to do that:
Uh, what? How on Earth did it go backwards when time was added to the timestamp? Let’s try this another way – just add the minutes to the timestamp and make a new datetime from that:
Now we get the correct answer.
The biggest problem, however, is serialization. There is no way to reliably recreate instances of the
datetime.tzinfo
class without knowing where they came from unlesspickle
is used. Some library specific logic could be used, like checking what file a dateutil timezone points to. Additionally, local-bound timezones are problematic in that if the project is moved to another server, it will start using that server’s local timezone, whatever it is. This may be what you want, or not. Either way this should be explicit.Even relying on pickle might have been borderline acceptable on APScheduler 3.x, but 4.0+ will offer a variety of serializers so this approach will not work.
If you want a more comprehensive explanation of the issue, have a look at the pytz documentation.
To precise my comment (and issue) the fact that when giving explicit timezone to apscheduler you have to use
pytz
stuff is not a problem (and I apologize for not reading the docs about it - tbh I don’t use this feature).My issue is that my standard datetimes with standard tzinfo does not work when given to
next_run_time
.