通过一个字符串,可以描述一系列时间点,以便程序定期启动,这件事 Quartz Cron
表达式已经做的不错了。它已经极强的扩展了 Linux Cron
的能力。
但是,还不够。譬如有一个比较常用的场景:
从今年下个月15号开始,每天的 8:00,10:30,14:40 各运行一次
为此,我定义了 NutzCron
,它扩展了 Quartz Cron
的语法,以便能适用更多的场景。
本代码库并不负责任务的定期支持,本代码库仅仅为你提供对 NutzCron
解析和解释和执行的能力。
诚然,就是你给我一个字符串,我给你解析成一个类 ZCron
即 ZCron
类有一个函数 toText
接受一个 i18n
的消息字符串模板集,它会把当前的设定根据你给的消息模板集,转成一个人类比较容易看懂的文字。本文后面章节会专门介绍这个消息模板集怎么设置。总之不太复杂啦,就是一个普通的JSON对象
对于一个 ZCron
对象,怎么执行呢?因为这是一个轻巧的解析库,我认为你只是需要通过这个表达式拿到一系列启动时间点。后面的事情,你也不需要我来关心。
那么我怎么给你一系列启动时间点呢?返回一个列表吗?不不不,我通常不会这么设计的,因为我觉得有点不够优雅(为啥不优雅?呃,我就认为不优雅,你咬我啊!!!)。
我这里的 执行 实际上是 填充。
即,你需要提供给我一个数组,对,你没看错,是一个数组。由于表达式的时间最多精确到秒,所以你给我的数组,最大长度不应该超过86400,即,一秒对应一个数组元素。
然后,你再给我一个对象,随便什么对象,我会把这个对象填到对应的数组格子里
@Str : `0 0 0 * * ?` <- 输入表达式
@Obj : ? <- 输入一个随便什么对象
@Arr : [ ][ ]..[ ][ ] <- 输入一个数组,通常是空白数组
@Date : 2017.. <- 输出一个日期
// 解析并执行
js: ZCron(@Str).fill(@Arr, @Obj, @Date)
java: new ZCron(@Str).fill(@Arr, @Obj, @Date)
// 你输入的 @Arr 会变成
[ ][ ][ ][Obj][ ]...[ ][Obj][ ][ ][ ]
那么通过遍历数组,你自然知道启动的时间了。
那么你可能会问,每次都要给你86400这么长的空数组吗?不用的,你其实可以给我譬如24长的数组,表示[00:00][01:00]..[22:00][23:00]
这24个时间点,如果任何一个时间点被表达式匹配,我就会填充对象。
这也就是我让你输入数组的好处,你可以通过改变数组的长度,将每天分成固定等份。譬如你给我48长度的数组,则表示[00:00][00:30][01:00][01:30]..[23:00][23:30]
这48个时间点。
下面我们来正式说说,这个表达式的语法。我先给你一个例子
0 0 0 * * ?
熟悉 quartz
的朋友马上会发现,这不就是 Quartz 嘛。
是的,表达式完全兼容 Quartz 表达式。
表达式的信息分作两部分
- 描述一天的时间点
- 限制日期
考虑到有的朋友对 Quartz 不熟,我这里先把 Quzrtz 的语法贴一下,NutzCron
完全兼容哦。
表达式是一个字符串,它有六个子表达式构成。这些子表达式用空格来分隔。
# 子表达式描述如下:
0) 秒(0~59)
1) 分钟(0~59)
2) 小时(0~23)
3) 天(月)(1~31,但是你需要考虑你月的天数)
4) 月(1~12)
5) 周(星期)(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)
6) 年 空 或 1970-2099,
# 特殊符号
'-' : 范围, 比如在子表达式(月),"1-4" 表示 2月到5月
"," : 列表分隔, 比如在子表达式(天-星期),"1,3" 表示 周日和周二
"*" : 代表所有可能的值
"/" : 用来指定数值的增量
> 例如: 在子表达式(分钟)里的 "0/15" 表示从第0分钟开始,每15分钟
> 又如: 在子表达式(分钟)里的 "3/20" 表示从第3分钟开始,每20分钟
(它和 "3,23,43" )的含义一样
"?" : 仅被用于天(月)和天(星期)两个子表达式,表示不指定值
"L" : 仅被用于天(月)和天(星期)两个子表达式,它是单词“last”的缩写
> 在天(月)子表达式中,“L”表示一个月的最后一天
>> 6L”表示这个月的倒数第6天
> 在天(星期)自表达式中,“L”表示一个星期的最后一天,也就是SAT
>> “FRIL”表示这个月的最一个星期五
"W" : 仅被用于天(月)子表达式,表示工作日
> "W" 为所有工作日
> "4W" 为距离本月第5日最近的工作日
> "4LW" 为距离当月倒数第4日最近的工作日
"#" : 仅用在天(星期)表示第几个
> 3#1 表示 第1个周二
> FRI#2 第2个周五
譬如*(ZCron.toText 自动生成)*:
0 0 0 * * ? : 每月的每天的0点0分0秒
0 0 ? * * ? : 每月的每天的每小时的0分0秒
0 ? ? * * ? : 每月的每天的每小时的每分钟的0秒
0 0 8-11,13-18 * * ? : 每月的每天的8点至11点,13点至18点0分0秒
0 0 0 7-13 JUL ? : 七月的7号至13号的0点0分0秒
0 0 0 1,3,5 * ? : 每月的1号,3号,5号的0点0分0秒
0 0 8/3 * * ? : 每月的每天的从8点开始每3小时0分0秒
0 0 0 4L * ? : 每月的倒数第4日的0点0分0秒
0 0 0 W * ? : 每月的所有工作日的0点0分0秒
0 0 0 1LW * ? : 每月的最后一日最近的工作日的0点0分0秒
0 0 0 6LW * ? : 每月的倒数第6日最近的工作日的0点0分0秒
0 0 0 * * 1-3 : 每月的周日至周二的每天0点0分0秒
0 0 0 * * 1,2-4,7 : 每月的周日,周一至周三,周六的每天0点0分0秒
0 0 0 * * FRI#2 : 每月的第2个周五的每天0点0分0秒
0 0 0 * * 2#4 : 每月的第4个周一的每天0点0分0秒
0 0/5 8,10-14,23 * * ? : 每月的每天的8点,10点至14点,23点从0分开始每5分钟0秒
0 0 0,1 * * ? : 每月的每天的0点,1点0分0秒
(扩展部分) (Quartz的时间部分) (Quartz的日期部分) (扩展部分)
... 0 0 0 * * ? ...
即,你可以在标准 Quartz 表达式前面或者后面增加扩展部分,它的语法具体为:
D[20170801,20170822) # D 开头限定日期区间(不能包括空格)
T[08:12,09:16] # T 开头限定时间区间(不能包括空格)
# 这个会结合 Quartz 的时间部分来确定启动时间点
T[08:12,09:16]{0/30m} # 在时间限定区间后面跟上 {offset/step} 表示一组时间点
# 譬如这个 0/30m 表示:
# 循环开始偏移量 0 表示从区间开始处开始
# 循环间隔30分钟,支持的单位为 s秒,m分,h小时
# 循环开始偏移量还是可以是:
# "10s" : 从区间开始时间偏移 10 秒
# "8m" : 从区间开始时间偏移 8 分钟
# "2h" : 从区间开始时间偏移 2 小时
# ">" : 以循环间隔为单位将全天均分,偏移至最近的一个时间点
# ">24m" : 以24分钟为单位将全天均分,偏移至最近的一个时间点
# 因此 -/30m 与 -30m/30m 的意义是一样的
# ">2h" : 2小时全天均分
# ">50s" : 50秒全天均分
# "2" : 根据循环间隔的时间单位一致
# ! 如果声明了这个那么 Quartz 的时间部分会被无视,
# 你可以不写,写了也没用
T{02:30,06:48,18:25} # T{..} 格式的为明确指定自由的启动时间点(不能包括空格)
# ! 如果声明了这个那么 Quartz 的时间部分会被无视,
# 你可以不写,写了也没用
------------------------------------------------
> 注意哦,T,D 开头的,后面跟着是区间表达式,中学数学学的区间没忘吧。开区间闭区间。
> 总之,`[` 表示闭,`(` 表示开
[34,100] 表示 n>=34 && n<=100
(34,100] 表示 n>34 && n<=100
[34,100) 表示 n>=34 && n<100
[34,] 表示 n>=34
[34,) 表示 n>=34
(34,] 表示 n>34
(34,) 表示 n>34
[,100] 表示 n<=100
(,100] 表示 n<=100
[,100) 表示 n<100
(,100) 表示 n<100
(100) 表示 n!=100
[100] 表示 n==100
(100] 表示 n==100
[100) 表示 n==100