<p>最可靠的方式是使用 github.com/robfig/cron/v3 的 Parse 方法进行合法性校验,它严格按 cron 规范执行词法和语义检查,能准确识别 @yearly、/5、0 0 1 * 等合法变体及非法输入如字段数错误、数值越界、非法字符等。</p>

用 github.com/robfig/cron/v3 的解析器做合法性校验最可靠
Go 标准库不提供 cron 表达式语法校验,直接手写正则几乎不可能覆盖所有合法变体(比如 @yearly、0 0 1 * *、*/5 * * * *、带空格的字段分隔等)。robfig/cron/v3 是事实标准,它的 Parse 函数会严格按 cron 规范执行词法和语义检查,比任何正则都准。
实操建议:
- 安装:
go get github.com/robfig/cron/v3 - 调用
cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow).Parse(<var>expr</var>)—— 注意要显式传入支持的字段位掩码,否则默认不支持秒字段,0 * * * * *会报错 - 只关心是否合法?忽略返回的
cron.Schedule,只看 error 是否为 nil - 注意:该库会拒绝含非法字符(如中文、全角空格)、字段数不对(不是 5 或 6 个)、数值越界(如月字段填 13)、逻辑冲突(如
0 0 32 * *)的表达式
别用正则匹配“看起来像 cron”的字符串
网上常见 ^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]))(\s+(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]))){4}$ 这类正则,它根本无法识别 @daily、0,30 * * * *、1-5 * * * *、*/15 9-17 * * 1-5 等合法写法,且对空格容忍度和字段边界处理极差。
典型错误现象:
立即学习“go语言免费学习笔记(深入)”;
-
Parse("@hourly")返回 error,但正则可能放行 -
"0 0 1 1 *" → "0 0 1 13 *"字段数相同,但后者月字段非法 —— 正则很难判断 13 是否超出范围 - 用户输入
"0 0 1 * * "(末尾空格)或"0\t0\t1\t*\t*"(tab 分隔),正则易漏判
区分“语法合法”和“语义合理”
Parse() 只保证语法合法,不保证调度有意义。例如 "0 0 32 * *" 会被拒绝(日期越界),但 "0 0 31 4 *" 会被接受(4 月没有 31 日,运行时跳过),"0 0 29 2 *" 在非闰年也会跳过。
如果业务需要更强约束(比如禁止使用 * 或不允许跨月日期),得额外加逻辑:
- 用
strings.Fields(expr)拆出字段,逐个检查是否含*、/、-、,等特殊符号 - 对日期字段(第 3 位)做静态范围预检:若值为数字,确认 ≤ 31;若为
*/N,确认步长 N ≤ 31 - 注意:不要试图在解析前做“合理性”过滤,容易误杀
"0 0 */2 * *"这类合法表达式
注意 v3 版本的兼容性陷阱
v3 默认启用秒字段(6 字段),而传统 cron 是 5 字段。如果你的系统只接受 5 字段格式,必须显式禁用秒解析:
- 错:直接用
cron.Parse(expr)→ 会把"0 0 * * *"当作缺少秒字段报错 - 对:用
cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow).Parse(expr) - v2 和 v3 的解析行为不同:v2 不校验字段值范围(如允许
0 0 0 * *),v3 会拒绝;升级时务必测试存量表达式 - 错误信息是
"field 3: invalid day of month value: 0"这类明确提示,可直接透出给用户定位问题
真正难的不是“怎么判断”,而是理解 cron 规范本身有多松散 —— 同一个表达式在不同实现里可能被接受或拒绝。用成熟 parser 是唯一省心方式,别给自己造轮子。


















