Cron 表达式解析,crontab表达式解析

目录

  • Cron 表达式解析,crontab表达式解析
  • 前言
  • 一、代码展示

前言

将类似
30 8 * * 1,4,6
这种的表达式解析为执行时间,获取下一次或者多次的执行时间。


一、代码展示

代码如下(示例):

import cn.hutool.core.date.DateUtil;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Set;
import java.util.TreeSet;import static java.util.Calendar.DATE;
import static java.util.Calendar.DAY_OF_YEAR;
/*** @author ******* Created on 2022/3/15.** Cron 表达式解析*/
public class CronParser {private String cronExp;public CronParser(String exp) {this.cronExp = exp;}public Date nextDate(Date start) {Calendar lastCal = Calendar.getInstance();lastCal.setTime(start);//上一次执行时间字段int lastYear = lastCal.get(Calendar.YEAR);int lastMonth = lastCal.get(Calendar.MONTH) + 1;int lastDay = lastCal.get(Calendar.DAY_OF_MONTH);int lastMonthDay = this.encodeMonthday(lastMonth, lastDay);int lastHour = lastCal.get(Calendar.HOUR_OF_DAY);int lastMinute = lastCal.get(Calendar.MINUTE);int lastSecond = lastCal.get(Calendar.SECOND);//下一次执行时间字段Integer newMonthDay = null;Integer newHour = null;Integer newMinute = null;Integer newYear = lastYear;//解析cron表达式String[] exps = cronExp.split("\\s+");CronRange minute = parseRange(exps[0], 0, 59);CronRange hour = parseRange(exps[1], 0, 23);CronRange day = parseRange(exps[2], 1, 31);CronRange month = parseRange(exps[3], 1, 12);CronRange week = parseRange(exps[4], 1, 7);CronRange monthDay = this.calMonthDay(month, day, week);if (monthDay.isEmpty()) {return null;}boolean isNotFound = false;if (monthDay.inRange(lastMonthDay)) {if (hour.inRange(lastHour)) {if (minute.inRange(lastMinute)) {newMinute = minute.getNextValue(lastMinute);}if (newMinute == null) {//如果分钟找不到,需要对小时进行递增newHour = hour.getNextValue(lastHour);isNotFound = newHour == null;newMinute = minute.getMin();} else {newHour = lastHour;}}if (newHour == null) {if (isNotFound) {//如果小时找不到,需要对天数进行递增if (monthDay.isAll()) {Calendar c = Calendar.getInstance();c.setTime(start);c.add(DATE, 1);newMonthDay = this.encodeMonthday(c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH));} else {//如果跨年了就找不到newMonthDay = monthDay.getNextValue(lastMonthDay);}} else {newMonthDay = lastMonthDay;}newHour = hour.getMin();newMinute = minute.getMin();} else {newMonthDay = lastMonthDay;}} else {//天如果不在范围内,需要对天进行递增newMonthDay = monthDay.getNextValue(lastMonthDay);newHour = hour.getMin();newMinute = minute.getMin();}if (newMonthDay == null) {//跨年newYear = newYear + 1;if (monthDay.isAll()) {//1月1日newMonthDay = 0x0101;} else {newMonthDay = monthDay.getMin();}newHour = hour.getMin();newMinute = minute.getMin();}Calendar newCal = Calendar.getInstance();newCal.set(Calendar.MONTH, this.decodeMonth(newMonthDay) - 1);newCal.set(Calendar.DAY_OF_MONTH, decodeDay(newMonthDay));newCal.set(Calendar.HOUR_OF_DAY, newHour);newCal.set(Calendar.MINUTE, newMinute);newCal.set(Calendar.SECOND, lastSecond);newCal.set(Calendar.YEAR, newYear);return newCal.getTime();}/*** 将月和日合并成一个int型整数* @param month* @param day* @return*/public int encodeMonthday(int month, int day) {return (day & 0x000000FF) | (month << 8 & 0x0000FF00);}/*** 解码月* @param monthDay* @return*/public int decodeMonth(int monthDay) {return (monthDay & 0x0000FF00) >> 8;}/*** 解码日* @param monthDay* @return*/public int decodeDay(int monthDay) {return (monthDay & 0x000000FF);}private CronRange calMonthDay(CronRange month, CronRange day, CronRange week) {CronRange monthDay = new CronRange();if (month.isAll() && day.isAll() && week.isAll()) {//如果都是全范围的就不进行计算monthDay.setReturnAll(true);return monthDay;}int[] monthDays = {31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};//如果是闰年就是29天monthDays[1] = Calendar.getInstance().getActualMaximum(DAY_OF_YEAR) > 365 ? 29 : 28;Set<Integer> rangeMonth = month.getRange();for (Integer m : rangeMonth) {for (int d = 1; d <= monthDays[m - 1]; ++d) {if (day.inRange(d)) {//判断周的逻辑if (!week.isAll()) {Calendar cal = Calendar.getInstance();cal.set(Calendar.MONTH, m - 1);cal.set(Calendar.DAY_OF_MONTH, d);int w = cal.get(Calendar.DAY_OF_WEEK) - 1;//周日-周六==>1-7w = w == 0 ? 7 : w;if (!week.inRange(w)) {continue;}}monthDay.addRange(this.encodeMonthday(m, d));}}}return monthDay;}/*** 解析表达式的取值范围和循环周期** @param exp* @param start* @param end* @return*/public CronRange parseRange(String exp, int start, int end) {String[] exps = exp.trim().split("/");CronRange range = new CronRange();if (exps.length > 1) {range.setCycle(Integer.parseInt(exps[1]));}if (exps[0].trim().length() == 0) {range.range(start, end);} else if ("*".equals(exps[0])) {range.range(start, end);range.setReturnAll(exps.length == 1);} else if (exps[0].contains("-")) {String[] ss = exps[0].split("-");range.range(Integer.parseInt(ss[0]), Integer.parseInt(ss[1]));} else if (exps[0].contains(",")) {String[] ss = exps[0].split(",");for (String s : ss) {range.addRange(Integer.parseInt(s));}} else {range.addRange(Integer.parseInt(exps[0]));}return range;}public static void main(String[] args) throws ParseException {String cronExp = "30 8 * * 1,4,6";CronParser parser = new CronParser(cronExp);String lastExecuteDateStr = "2022-03-12 22:23:22";Date date = DateUtil.date();SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm");Date lastExecuteDate = fmt.parse(lastExecuteDateStr);for (int i = 0; ; ++i) {lastExecuteDate = parser.nextDate(lastExecuteDate);if (DateUtil.compare(lastExecuteDate,date)>0) {System.out.println(fmt.format(lastExecuteDate));break;}System.out.println(fmt.format(lastExecuteDate));}}
}class CronRange {private Set<Integer> range = new TreeSet<>();private Integer cycle;private Integer max = null;private Integer min = null;private Boolean returnAll = false;public CronRange range(int start, int end) {for (int i = start; i <= end; ++i) {this.addRange(i);}return this;}public CronRange addRange(int value) {max = (max == null || value > max) ? value : max;min = (min == null || value < min) ? value : min;this.range.add(value);return this;}public Set<Integer> getRange() {return range;}public void setCycle(Integer cycle) {this.cycle = cycle;}public boolean inRange(int value) {return returnAll ? true : range.contains(value);}public boolean isEmpty() {return !returnAll && range.isEmpty();}public Integer getNextValue(int lastValue) {Integer value = null;if (this.cycle != null) {value = this.cycle + lastValue;while (!inRange(value)) {value = value + this.cycle;if (value > max) {value = null;break;}}} else {value = this.getNextMin(lastValue);}return value;}private Integer getNextMin(int value) {Integer[] integers = range.toArray(new Integer[range.size()]);Integer minValue = null;for (int i = 0; i < integers.length; ++i) {if (integers[i] > value) {minValue = integers[i];break;}}return minValue;}public Boolean isAll() {return returnAll;}public void setReturnAll(Boolean returnAll) {this.returnAll = returnAll;}public Integer getMin() {return min;}
}

Cron 表达式解析,crontab表达式解析相关推荐

  1. Go 学习笔记(77)— Go 第三方库之 cronexpr(解析 crontab 表达式,定时任务)

    cronexpr 支持的比 Linux 自身的 crontab 更详细,可以精确到秒级别. ​ 1. 实现方式 cronexpr 表达式从前到后的顺序如下所示: 字段类型 是否为必须字段 允许的值 允 ...

  2. Windows下的Crontab表达式解析DLL的使用

    Linux的crontab工具特别的好用,正好现在工作总有好多定时执行的事 用Windows的定时任务觉得特别Low,哈哈,用C#写个任务触发器 然后再用上Crontab表达式解析DLL,觉得马上就高 ...

  3. 在线常用crontab表达式大全验证解析

    在线常用crontab表达式大全验证解析 在线常用crontab表达式大全验证解析 本工具可以在线模拟Crontab表达式的执行时间并且收集整理了常用的Crontab表达式. 本工具可以在线模拟Cro ...

  4. 4 三元表达式 列表解析 生成器表达式

    三元表达式 #!/usr/bin/env python3 # _*_ coding:utf-8 _*_ # @File : 三元表达式 # @Version : 1.0'''三元表达式能实现简单的 i ...

  5. 三元表达式列表解析生成器表达式

    三元表达式 列表解析 生成器表达式 三元表达式 在作简单的判断时,三元表达式能简化代码: def max(x, y):if x > y:return xelse:return y # 这个函数可 ...

  6. 用栈实现算术表达式 java_java的栈和用栈来解析算术表达式

    废话不多说,直接上代码, 栈是最简单的数据及结构, 可以使用数组实现也可以用链表实现. 并且有对泛型的支持.接口点击(此处)折叠或打开public interface StackE> {     ...

  7. Java解析SQL生成语法树_Atitit.sql ast 表达式 语法树 语法 解析原理与实现 java php c#.net js python...

    Atitit.sql ast 表达式 语法树 语法 解析原理与实现java php c#.net js python 1.1.Sql语法树ast如下图锁死 2.SQL语句解析的思路和过程 2.1.le ...

  8. Spring Aop中解析spel表达式,实现更灵活的功能

    前言 在Spring Aop中,我们可以拿到拦截方法的参数,如果能结合spel表达式,就能实现更加灵活的功能.典型的实现有Spring的缓存注解: @Cacheable(value = "u ...

  9. python天天向上代码解析-python列表解析和生成器表达式

    列表解析在需要改变列表而不是需要新建某列表时,可以使用列表解析.列表解析表达式为: [expr for iter_var in iterable] [expr for iter_var in iter ...

  10. javascript解析_使用JavaScript解析数学表达式

    javascript解析 by Shalvah 由Shalvah 使用JavaScript解析数学表达式 (Parsing math expressions with JavaScript) A wh ...

最新文章

  1. 九十二、Python爬取深圳租房信息小案例
  2. SAP CRM One Order scheduline buffer handling
  3. CAD数据与ArcGIS数据的互转换(转载)
  4. 【GIS风暴】GIS拓扑关系原理详解
  5. 创建基于密码的加密密钥
  6. java服务器与客户端项目,Java项目中用于服务器和客户端软件包的共...
  7. 企业中该如何防止僵尸网络的入侵?
  8. ROS-Academy-for-Beginners之ORB-SLAM2 双目视觉初探
  9. C# WinForm技巧“将Form嵌入到Panel”
  10. 泰迪杯特等奖思路(教育平台线上课程用户行为分析(含数据可视化处理))-思路篇
  11. ADC学习(4)—— 电压比较器
  12. 体脂的计算Java_简单测试体脂率的两种经验公式
  13. 【Hexo】Next主题添加全局播放翻页不间断的网易云音乐
  14. java文档翻译,将word文件翻译该怎么操作?
  15. C4D模型工具—连接点/边
  16. Nginx配置反向代理_飘云羽逸_新浪博客
  17. 基于深度学习的单视图三维重建算法学习路线
  18. java 内存回收参数_JVM内存模型及垃圾回收的研究总结
  19. Android课程设计倒计时app,999倒计时计时器课程设计.docx
  20. 苹果xr怎么截屏_苹果怎么截图?教你各种iPhone机型截屏方法

热门文章

  1. 产品从无到有的方法框架——5MVVP框架,实际是如何运作的?
  2. Ubuntu/Linux备份/还原系统
  3. Java实现KMP算法
  4. 2017国民行业分类sql-存储过程_存储函数-MySQL
  5. avast6.0网络安全软件破解至2050年_avast激活码_avast有效激活
  6. 职场怪谈:技术面试过了一般HR多久联系?
  7. Win10安装和配置IIS web服务器环境来运行ASP(动态服务器页面)脚本
  8. Linux操作系统常用基本命令
  9. Android 集成科大讯飞语音并实现语音识别
  10. python java爬虫_java爬虫与python爬虫对比