CRON时间表达式

CRON时间表达式

我们知道在Linux上的任务计划可以通过cron服务管理,cron服务设置任务运行时间有着自定义的时间表达式,形如

1
2
# m h  dom mon dow   command
58 8 * * 1-5 /root/run.sh

这条记录表示在每周一到周五的08:58自动运行/root/run.sh脚本,除去command,我们看到时间表达式有m, h, dom, mon, dow几个部分,分别表示分钟、小时、每月几号、月份、星期几。几号和星期几通常是二选一,指定了一个另一个就会指定成*表示任意,也可以都指定成*,则表示每天。

dow1-5表示周一到周五,也可以用逗号分割列举具体星期几,例如1,3,5表示每周一三五,除了周几,其他时间项也都可以使用此表示法。

除了用-表示范围,还可以用/表示频率例如:

1
2
# m   h   dom mon dow   command
0/5 9-14 * * 1-5 /root/run.sh

表示每周一~周五,小时为9点到14点,从0分开始每5分钟运行一次脚本,这样每天第一次运行的时间是09:00,每天最后一次运行的时间是14:55,中间都是每隔5分钟运行一次。

扩展CRON时间表达式

增加秒

一些程序允许使用扩展的CRON时间表达式,一般会增加的表示

可能通过第一位也可能通过最后一位表示。

1
s  m   h   dom mon dow

1
m   h   dom mon dow  s

具体要阅读文档来查询。

增加随机

还有一种扩展是增加随机性指定,通过以字母H开头表示

例如

1
H(55-59) 8  *  *  1-5  /root/run.sh

表示周一到周五,每天[08:55, 09:00)之间的一个随机时间运行/root/run.sh,这样通常是为了缓解多个任务同时启动的压力。

Python中通过带时区的datetime使用croniter

1
2
3
4
5
6
7
8
9
10
11
12
13
from croniter import croniter
from datetime import datetime
from dateutil import tz

now = datetime.now().replace(tzinfo=tz.gettz()) # 得到当前时间(带时区)
cron = croniter('0/5 9-14 * * 1-5', now) # 初始化cron对象

dt = cron.get_next(datetime) # 取得下次触发时间,实际上做的是迭代器去next()然后返回
print(dt)
dt = cron.get_next(datetime) # 所在第二次调用的时候,迭代器继续next(),这里返回的是第二次触发的时间
print(dt)
dt = cron.get_prev(datetime) # 同理,这里将返回第一次触发的时间,而不是初始化时间之前的上一次触发时间
print(dt)

有时候我们即需要上次触发时间,又需要下次触发时间,要么我们理解了迭代器的方式通过取prev再取next的next,要么我们创建两个cron对象分别取prev和next。

croniter允许通过datetime对象或者timestamp(即time.time()的返回值,float类型)来使用cron对象,但是我们的时间表达式是带有时区概念的,所以cron对象必须明确知道我们的时区,否则将不能正确工作,一种方式是我们使用datetime初始化cron对象,取next或prev的时候也使用datetime类型接收,这时候即使时区不明确也不要紧,但是不能使用timestamp接收next和prev的返回值,否则cron对象会默认使用UTC时区导致返回的float值不正确,如果我们使用带时区的datetime来初始化cron对象则没有这个问题,上面例子就是通过带时区的方式初始化的。

另外,python的cron对象是同时支持秒扩展和随机扩展的,秒放在最后一位,使用随机需要在初始化时指定hash_id,不同的hash_id将影响随机结果,例如我还是上面的例子,但是我需要触发时间不在分钟的整点,而在这一分钟内随机的秒数上,就可以写成下面这样

1
cron = croniter('0/5 9-14 * * 1-5 H(0-59)', now, hash_id="hash_id")