cron表达式解析 + robfig/cron 源码剖析

robfiig/cron 源码剖析

Cron 表达式

参考wiki
https://en.wikipedia.org/wiki/Cron

robfiig/cron项目信息

下载地址

https://github.com/robfig/cron.git

文件目录讲解

constantdelay.go      #一个最简单的秒级别定时系统。与cron无关
constantdelay_test.go #测试
cron.go               #Cron系统。管理一系列的cron定时任务(Schedule Job)
cron_test.go          #测试
doc.go                #说明文档
LICENSE               #授权书 
parser.go             #解析器,解析cron格式字符串城一个具体的定时器(Schedule)
parser_test.go        #测试
README.md             #README
spec.go               #单个定时器(Schedule)结构体。如何计算自己的下一次触发时间
spec_test.go          #测试

robfiig/cron 目前实现的需求

doc.go

CRON Expression Format
A cron expression represents a set of times, using 6 space-separated fields.
Field name   | Mandatory? | Allowed values  | Allowed special characters
----------   | ---------- | --------------  | --------------------------
Seconds      | Yes        | 0-59            | * / , -
Minutes      | Yes        | 0-59            | * / , -
Hours        | Yes        | 0-23            | * / , -
Day of month | Yes        | 1-31            | * / , - ?
Month        | Yes        | 1-12 or JAN-DEC | * / , -
Day of week  | Yes        | 0-6 or SUN-SAT  | * / , - ?
Predefined schedules

You may use one of several pre-defined schedules in place of a cron expression.

Entry                  | Description                                | Equivalent To
-----                  | -----------                                | -------------
@yearly (or @annually) | Run once a year, midnight, Jan. 1st        | 0 0 0 1 1 *
@monthly               | Run once a month, midnight, first of month | 0 0 0 1 * *
@weekly                | Run once a week, midnight on Sunday        | 0 0 0 * * 0
@daily (or @midnight)  | Run once a day, midnight                   | 0 0 0 * * *
@hourly                | Run once an hour, beginning of hour        | 0 0 * * * *

可以看到这里并没有实现 L , W , # 这些特殊字符。 至于原因,下面将具体实现代码的时候会给出。

cron表达式解析

cron 的BNF表达式以及引出的解析方式

首先,让我试着使用EBNF来定义下cron 表达式(不是很严谨。。。)


<non_zero>      ::= "1" | "2"  | "3" | "4" | "5" | "6" | "7" | "8" | "9"
<num>           ::= <non_zero> | "0"
<number>        ::= <non_zero> {<num>} 
<normal_item>   ::= "*" | <number>  |  <number> "-"<number> | <number> "/" <number>
<days_item>     ::= <normal_item> | "?" | <number>"L" | <number>"W" |  <number>"#"
<item>          ::= <normal_item> | <days_item>;
<items>         ::= {<item>","} <item> ;
<cron>          ::= <items>" "<items>" "<items>" "<items>" "<items>" "<items>

至此, 如果我打算解析一个cron表达式, 应该遵循以下步骤 :

  1. cron利用空白拆解出独立的items
  2. items利用,拆解出item
  3. item 利用穷举法一一检测( 符合且仅符合下面的一条才能合法) :
    1. 是否仅有一个字符是 * 或者
    2. 是否可以使用/或者-分解为俩数字 。
    3. 是否以数字加L或者W结尾。
    4. 纯数字。
  4. 将规则一一描述,完成规则解析

如何表示一个独立的规则

cron表达式是用来表示一系列时间的,而时间是无法逃脱自己的区间的 , 分,秒 0 - 59 , 时 0 - 23 , 天/月 0 - 31 , 天/周 0 - 6 , 月0 - 11 。 这些本质上都是一个点集合,或者说是一个整数区间。 那么对于任意的整数区间 , 可以描述cron的如下部分规则。

  • * | ? 任意 , 对应区间上的所有点。 ( 额外注意 日/周 , 日 / 月 的相互干扰。)
  • 纯数字 , 对应一个具体的点。
  • / 分割的两个数字 a , b, 区间上符合 a + n * b 的所有点 ( n >= 0 )。
  • - 分割的两个数字, 对应这两个数字决定的区间内的所有点。
  • L | W 需要对于特定的时间特殊判断, 无法通用的对应到区间上的点。

至此, robfig/cron为什么不支持 L | W的原因已经明了了。去除这两条规则后, 其余的规则其实完全可以使用点的穷举来通用表示。 考虑到最大的区间也不过是60个点,那么使用一个uint64的整数的每一位来表示一个点便很合适了。下面是robfig/cron的表示方法 :

/* 
   ------------------------------------------------------------
   第64位标记任意 , 用于 日/周 , 日 / 月 的相互干扰。
   63 - 0 为 表示区间 [63 , 0] 的 每一个点。
   ------------------------------------------------------------ 

   假设区间是 0 - 63 , 则有如下的例子 : 

   比如  0/3 的表示如下 : 
   * / ?       
   +---+--------------------------------------------------------+
   | 0 | 1 0 0 1 0 0 1  ~~  ~~                    1 0 0 1 0 0 1 |
   +---+--------------------------------------------------------+   
        63 ~ ~                                           ~~ 0 

   比如  2-5 的表示如下 : 
   * / ?       
   +---+--------------------------------------------------------+
   | 0 | 0 0 0 0 ~  ~      ~~            ~    0 0 0 1 1 1 1 0 0 |
   +---+--------------------------------------------------------+   
        63 ~ ~                                           ~~ 0 

  比如  * 的表示如下 : 
   * / ?       
   +---+--------------------------------------------------------+
   | 1 | 1 1 1 1 1 ~  ~                  ~    1 1 1 1 1 1 1 1 1 |
   +---+--------------------------------------------------------+   
        63 ~ ~                                           ~~ 0 
*/

定时器的基本功能

一个时间是否符合规则

有一个规则后, 判断一个时间点是否符合规则其实就是对应位是否为1 。

预判断下一个符合规则的时间

给定一个时间后, 寻找下一个符合符合规则的时间也很简单 :

  • 从大到小,依次
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值