Skip to content

Commit a398883

Browse files
committed
schedule: make datetime objs timezone-aware and UTC default
* fix mock now() and tests with a timezone * allow flake8-max-line-length to be 90 * fixes for lack of datetime.timezone in python 2.7 (simple UTC) Signed-off-by: Steve Arnold <[email protected]>
1 parent 7ab021c commit a398883

File tree

4 files changed

+48
-10
lines changed

4 files changed

+48
-10
lines changed

schedule/__init__.py

+15-7
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@
4444
import random
4545
import time
4646

47+
try:
48+
from datetime import timezone
49+
utc = timezone.utc
50+
except ImportError:
51+
from schedule.timezone import UTC
52+
utc = UTC()
53+
54+
4755
logger = logging.getLogger('schedule')
4856

4957

@@ -149,7 +157,7 @@ def idle_seconds(self):
149157
:return: Number of seconds until
150158
:meth:`next_run <Scheduler.next_run>`.
151159
"""
152-
return (self.next_run - datetime.datetime.now()).total_seconds()
160+
return (self.next_run - datetime.datetime.now(utc)).total_seconds()
153161

154162

155163
class Job(object):
@@ -191,7 +199,7 @@ def __lt__(self, other):
191199

192200
def __repr__(self):
193201
def format_time(t):
194-
return t.strftime('%Y-%m-%d %H:%M:%S') if t else '[never]'
202+
return t.strftime('%Y-%m-%d %H:%M:%S %Z') if t else '[never]'
195203

196204
timestats = '(last run: %s, next run: %s)' % (
197205
format_time(self.last_run), format_time(self.next_run))
@@ -396,7 +404,7 @@ def should_run(self):
396404
"""
397405
:return: ``True`` if the job should be run now.
398406
"""
399-
return datetime.datetime.now() >= self.next_run
407+
return datetime.datetime.now(utc) >= self.next_run
400408

401409
def run(self):
402410
"""
@@ -406,7 +414,7 @@ def run(self):
406414
"""
407415
logger.info('Running job %s', self)
408416
ret = self.job_func()
409-
self.last_run = datetime.datetime.now()
417+
self.last_run = datetime.datetime.now(utc)
410418
self._schedule_next_run()
411419
return ret
412420

@@ -423,7 +431,7 @@ def _schedule_next_run(self):
423431
interval = self.interval
424432

425433
self.period = datetime.timedelta(**{self.unit: interval})
426-
self.next_run = datetime.datetime.now() + self.period
434+
self.next_run = datetime.datetime.now(utc) + self.period
427435
if self.start_day is not None:
428436
assert self.unit == 'weeks'
429437
weekdays = (
@@ -454,15 +462,15 @@ def _schedule_next_run(self):
454462
# If we are running for the first time, make sure we run
455463
# at the specified time *today* (or *this hour*) as well
456464
if not self.last_run:
457-
now = datetime.datetime.now()
465+
now = datetime.datetime.now(utc)
458466
if (self.unit == 'days' and self.at_time > now.time() and
459467
self.interval == 1):
460468
self.next_run = self.next_run - datetime.timedelta(days=1)
461469
elif self.unit == 'hours' and self.at_time.minute > now.minute:
462470
self.next_run = self.next_run - datetime.timedelta(hours=1)
463471
if self.start_day is not None and self.at_time is not None:
464472
# Let's see if we will still make that time we specified today
465-
if (self.next_run - datetime.datetime.now()).days >= 7:
473+
if (self.next_run - datetime.datetime.now(utc)).days >= 7:
466474
self.next_run -= self.period
467475

468476

schedule/timezone.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import datetime
2+
3+
4+
class UTC(datetime.tzinfo):
5+
"""tzinfo derived concrete class named "UTC" with offset of 0"""
6+
# can be configured here
7+
_offset = datetime.timedelta(seconds=0)
8+
_dst = datetime.timedelta(0)
9+
_name = "UTC"
10+
11+
def utcoffset(self, dt):
12+
return self.__class__._offset
13+
14+
def dst(self, dt):
15+
return self.__class__._dst
16+
17+
def tzname(self, dt):
18+
return self.__class__._name

test_schedule.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
import schedule
1212
from schedule import every
1313

14+
try:
15+
from datetime import timezone
16+
utc = timezone.utc
17+
except ImportError:
18+
from schedule.timezone import UTC
19+
utc = UTC()
20+
1421

1522
def make_mock_job(name=None):
1623
job = mock.Mock()
@@ -36,9 +43,10 @@ def today(cls):
3643
return cls(self.year, self.month, self.day)
3744

3845
@classmethod
39-
def now(cls):
46+
def now(cls, tz=None):
4047
return cls(self.year, self.month, self.day,
41-
self.hour, self.minute)
48+
self.hour, self.minute).replace(tzinfo=tz)
49+
4250
self.original_datetime = datetime.datetime
4351
datetime.datetime = MockDate
4452

@@ -258,7 +266,8 @@ def test_next_run_property(self):
258266
every().hour.do(hourly_job)
259267
assert len(schedule.jobs) == 2
260268
# Make sure the hourly job is first
261-
assert schedule.next_run() == original_datetime(2010, 1, 6, 14, 16)
269+
assert schedule.next_run() == original_datetime(2010, 1, 6, 14, 16,
270+
tzinfo=utc)
262271
assert schedule.idle_seconds() == 60 * 60
263272

264273
def test_cancel_job(self):

tox.ini

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ envlist = py27, py36, docs
66
3.5 = py35, docs
77
3.6 = py36, docs
88

9+
[flake8]
10+
max-line-length = 90
11+
912
[testenv]
1013
deps = -rrequirements-dev.txt
1114
commands =

0 commit comments

Comments
 (0)