使用 Cron 触发器

引言

cron 是一个存在已久的 UNIX 工具,其调度能力强大且经过验证。CronTrigger 类正是基于 cron 的调度能力构建的。

CronTrigger 使用“cron 表达式”,能够创建如“每周一至周五上午 8 点”或“每月最后一个周五的 1:30am”这样的触发时间表。

cron 表达式功能强大,但也可能相当令人困惑。本教程旨在揭开创建 cron 表达式的一些神秘面纱,为用户提供一个参考资料,以便在不得不在论坛或邮件列表中提问之前可以查阅。

格式

cron 表达式是一个由 6 或 7 个字段组成,字段间以空格分隔的字符串。字段可以包含任何允许的值,以及该字段允许的各种特殊字符组合。这些字段如下:

字段名 必填 允许的值 允许的特殊字符
秒钟 0-59 , - * /
分钟 0-59 , - * /
小时 0-23 , - * /
月份中的日期 1-31 , - * ? / L W
月份 1-12 或 JAN-DEC , - * /
星期中的哪天 1-7 或 SUN-SAT , - * ? / L #
年份 空、1970-2099 , - * /

因此,cron 表达式可以很简单,如:* * * * ? *

或者更复杂,如:0/5 14,18,3-39,52 * ? JAN,MAR,SEP MON-FRI 2002-2010

特殊字符

  • *(“所有值”)- 用于选择字段内的所有值。例如,分钟字段中的 * 表示 “每分钟”。
  • ?(“无特定值”)- 当需要在一个字段中指定某些内容,但在另一个允许该字符的字段中不关心时很有用。例如,如果我希望触发器在每个月的特定一天(比如 10 号)触发,但不在乎这一天是周几,我会在日-月字段中放 10,而在星期中放 ?。见下方示例以澄清。
  • - - 用于指定范围。例如,小时字段中的 10-12 意味着“10 点、11 点和 12 点”。
  • , - 用于指定额外的值。例如,星期中的哪天字段中的 MON,WED,FRI 意味着 “周一、周三、周五”。
  • / - 用于指定递增。例如,秒字段中的 0/15 意味着“0 秒、15 秒、30 秒和 45 秒”。而秒字段中的 5/15 意味着 “5 秒、20 秒、35 秒和 50 秒”。你也可以在 “字符-” 之后指定 /,在这种情况下相当于在 / 前有 0。日-月字段中的 1/3 意味着 “每月的第 1 天开始每隔 3 天触发一次”。
  • L(“最后”)- 在其允许的两个字段中具有不同的意义。例如,日-月字段中的 L 表示 “月份的最后一天” —— 1 月的 31 日,非闰年的 2 月的 28 日。如果单独在星期中的哪天字段使用,它简单表示 “7” 或 “周六”。但如果在星期中的哪天字段的其他值之后使用,它意味着 “月份的最后一个 xxx 天” —— 例如 6L 表示 “月份的最后一个周五”。你还可以指定离每月最后一天的偏移量,如 L-3 意味着日历月的倒数第三天。使用 L 选项时,重要的是不要指定值的列表或范围,否则你会得到令人困惑/意外的结果。
  • W(“工作日”)- 用于指定给定日期最近的工作日(周一至周五)。例如,如果在日-月字段中指定 15W,含义是:“接近当月 15 日的工作日”。所以如果 15 日是周六,触发器会在 14 日周五触发。如果 15 日是周日,触发器将在 16 日周一触发。如果 15 日是周二,则它将在 15 日周二触发。然而,如果你指定日-月字段的值为 1W,并且 1 日是周六,触发器将触发在 3 日周一,因为它不会“跨越”一个月天数的边界。字符 W 只能在日-月字段指定单个日期时使用,不能是范围或日期列表。

提示:字符 LW 也可以在日-月字段中组合使用,形成 LW,这表示 “每月的最后一个工作日”。

  • # - 用于指定 “每月的第 n 个” 某天。例如,星期中的哪天字段中的值 6#3 意味着 “每月的第三个周五”(天 6=周五,#3 = 该月的第三个)。其他示例:2#1 = 每月的第一个周一,4#5 = 每月的第五个周三。注意,如果你指定 #5,而该月没有给定的星期几的第 5 个,那么那个月就不会发生触发。

提示:合法字符、月份和星期几的名称不区分大小写。MON 与 mon 相同。

示例

以下是一些完整的示例:

表达式 含义
0 0 12 * * ? 每天中午 12 点触发
0 15 10 ? * * 每天上午 10:15 触发
0 15 10 * * ? 每天上午 10:15 触发
0 15 10 * * ? * 每天上午 10:15 触发
0 15 10 * * ? 2005 2005 年每天上午 10:15 触发
0 * 14 * * ? 每天下午 2 点开始,每分钟触发,直到 2:59,每天
0 0/5 14 * * ? 每天下午 2 点开始,每 5 分钟触发,直到 2:55,每天
0 0/5 14,18 * * ? 每天下午 2 点和 6 点开始,每 5 分钟触发,分别直到 2:55 和 6:55
0 0-5 14 * * ? 每天下午 2 点开始,每分钟触发,直到 2:05,每天
0 10,44 14 ? 3 WED 每年三月的每个周三,在下午 2:10 和 2:44 触发
0 15 10 ? * MON-FRI 每周一至周五上午 10:15 触发
0 15 10 15 * ? 每月 15 日上午 10:15 触发
0 15 10 L * ? 每月最后一天上午 10:15 触发
0 15 10 L-2 * ? 每月倒数第二天上午 10:15 触发
0 15 10 ? * 6L 每月最后一个周五上午 10:15 触发
0 15 10 ? * 6L 2002-2005 2002 年至 2005 年,每月最后一个周五上午 10:15 触发
0 15 10 ? * 6#3 每月第三个周五上午 10:15 触发
0 0 12 1/5 * ? 每月第一天开始,每隔 5 天的中午 12 点触发,每月
0 11 11 11 11 ? 每年 11 月 11 日上午 11:11 触发

提示:注意 ?* 在星期中的哪天和日-月字段中的效果!

注意

警告:
同时指定星期中的哪天和日-月的具体值的支持并不完整(目前你需要在这两个字段中的一个使用 ? 字符)。
警告:
在本地实行 “夏令时” 变更的早上时间段内设置触发时间要小心(对于美国地区,通常是凌晨 2 点前后的一小时——因为时间的调整可能导致跳过或重复,取决于时间是向前还是向后移动。你可能会发现维基百科的这个条目有助于了解适用于你的 时区 的详细信息。
在本文档中