项目上有个功能要手动配置cron表达式用于定时调度,为了方便对cron表达式不太熟悉的操作人员使用,编写了一个解析cron表达式为中文白话的方法,支持数字、符号、字母组成的表达式,不支持包含英文缩写。用的是比较笨的方式一个一个解析,略显粗劣,供大家参考交流。
首先要引入依赖,这个是为了借助其他组件来校验cron表达式的格式是否正确,这样可以专注于表达式的解析
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
然后就是代码了
首先是入口方法,做cron表达式的校验,取出各域的值调用解析方法
public static String cronParse(String cron) {
StringBuilder stringBuilder = new StringBuilder();
boolean monthFlag = false;
boolean dayFlag = false;
boolean hourFlag = false;
boolean minuteFlag = false;
//通过new CronExpression对象的形式验证cron表达式格式是否正确
try {
CronExpression expression = new CronExpression(cron);
} catch (Exception e) {
StackTraceElement[] stackTrace = e.getStackTrace();
stringBuilder.append("表达式解析失败:").append(Arrays.toString(stackTrace));
return stringBuilder.toString();
}
List<String> cronList = Arrays.asList(cron.split(" "));
//解析月
StringBuilder month = splice(cronList, 4, false, "年",12, "月");
if(month != null){
monthFlag = true;
stringBuilder.append(month);
}
//日
// L这个月最后一天
// 15W 本月最接近15号的工作日
// LW 本月最后一个工作日
StringBuilder day = splice(cronList, 3, monthFlag, "月", 31, "日");
if(day != null){
dayFlag = true;
stringBuilder.append(day);
}
//周
// L这一周的最后一天 5L 这个月的最后一个星期五
// # 6#3 第三个周五
StringBuilder week = spliceWeek(cronList, 5, monthFlag, "月", 7, "月");
if(week != null){
dayFlag = true;
stringBuilder.append(week);
}
//时
StringBuilder hour = splice(cronList, 2, dayFlag, "日", 23, "时");
if(hour != null){
hourFlag = true;
stringBuilder.append(hour);
}
//分
StringBuilder minute = splice(cronList, 1, hourFlag, "时", 59, "分");
if(minute != null){
minuteFlag = true;
stringBuilder.append(minute);
}
//秒
StringBuilder second = splice(cronList, 0, minuteFlag, "分", 59, "秒");
if(second != null){
stringBuilder.append(second);
}
return stringBuilder.toString();
}
解析方法解析月 日 时 分 秒
/**
*
* @param cronList cron表达式转list
* @param index 当前解析的域的位置
* @param superFlag 当前解析等级是否需要带上级 如表达式表达每小时执行,那么无需解析为每日每小时执行,可不带上级日
* @param superLevel 前解析等级的上级 如月的上级是年
* @param bigSize 当前解析等级最大的数 如月最大是12
* @param levelShort 当前等级的名称简写
* @return
*/
private static StringBuilder splice(List<String> cronList, int index, Boolean superFlag, String superLevel, Integer bigSize, String levelShort){
StringBuilder stringBuilder = new StringBuilder();
String cron = cronList.get(index);
//*或者?无需解析
if(!cron.contains("*") && !cron.contains("?")){
// "/"代表从某时刻开始,每隔多久执行一次
if(cron.contains("/")){
String[] split = cron.split("/");
int begin = Integer.parseInt(split[0]);
int interVal = Integer.parseInt(split[1]);
//如果/前的数是当前域的最小值且间隔时间是1,则表示每..执行
if(interVal == 1){
if((begin == 0 && index != 4) || (begin == 1 && index == 4)){
stringBuilder.append("每").append(levelShort);
}
}else {
if(superFlag){
stringBuilder.append("的");
}else {
stringBuilder.append("每").append(superLevel).append("的");
}
for (int i = 0; begin <= bigSize; i++){
if(i == 0){
stringBuilder.append(begin).append(levelShort);
}else {
stringBuilder.append(",").append(begin).append(levelShort);
}
begin += interVal;
}
}
// "-"表示范围
}else if(cron.contains("-")){
String[] split = cron.split("-");
Integer begin = Integer.parseInt(split[0]);
Integer end = Integer.parseInt(split[1]);
if(superFlag){
stringBuilder.append("的");
}else {
stringBuilder.append("每").append(superLevel).append("的");
}
for (int i = 0; begin <= end; i++){
if(i == 0){
stringBuilder.append(begin).append(levelShort);
}else {
stringBuilder.append(",").append(begin).append(levelShort);
}
begin++;
}
// ","表示枚举
}else if(cron.contains(",")){
List<String> list = Arrays.asList(cron.split(","));
if(superFlag){
stringBuilder.append("的");
}else {
stringBuilder.append("每").append(superLevel).append("的");
}
for (int i = 0; i < list.size(); i++){
String num = list.get(i);
if(i == 0){
stringBuilder.append(num).append(levelShort);
}else {
stringBuilder.append(",").append(num).append(levelShort);
}
}
}
//日
// L这个月最后一天
// 15W 本月最接近15号的工作日
// LW 本月最后一个工作日
else if(index == 3 && "L".equals(cron)){
if(superFlag){
stringBuilder.append("的").append("最后一天");
}else {
stringBuilder.append("每").append(superLevel).append("最后一天");
}
}else if(index == 3 && "LW".equals(cron)){
if(superFlag){
stringBuilder.append("的").append("最后一个工作日");
}else {
stringBuilder.append("每").append(superLevel).append("最后一个工作日");
}
}else if(index == 3 && cron.contains("W")){
String dayNear = cron.substring(0, cron.length() - 1);
if(superFlag){
stringBuilder.append("的").append("最接近").append(dayNear).append("日的工作日");
}else {
stringBuilder.append("每").append(superLevel).append("最接近").append(dayNear).append("日的工作日");
}
}
//周
// L这一周的最后一天 5L 这个月的最后一个星期五
// # 6#3 第三个周五
else if(index == 5 && "L".equals(cron)){
if(superFlag){
stringBuilder.append("的").append("每个星期日");
}else {
stringBuilder.append("每").append(superLevel).append("每个星期日");
}
}
else {
if(superFlag){
stringBuilder.append("的").append(cron).append(levelShort);
}else {
stringBuilder.append("每").append(superLevel).append(cron).append(levelShort);
}
}
return stringBuilder;
}else {
return null;
}
}
表达式最后一位需要单独解析
private static StringBuilder spliceWeek(List<String> cronList, int index, Boolean superFlag, String superLevel, Integer bigSize, String levelShort){
StringBuilder stringBuilder = new StringBuilder();
String cron = cronList.get(index);
if(!cron.contains("*") && !cron.contains("?")){
if(cron.contains("/")){
String[] split = cron.split("/");
Integer begin = Integer.parseInt(split[0]);
Integer interVal = Integer.parseInt(split[1]);
if(begin == 0 && interVal == 1){
stringBuilder.append("每").append("一天");
}else {
if(superFlag){
stringBuilder.append("的");
}else {
stringBuilder.append("每").append(superLevel).append("的");
}
for (int i = 0; begin <= bigSize; i++){
if(i == 0){
stringBuilder.append(begin).append(levelShort);
}else {
stringBuilder.append(",").append(numToWeek(begin));
}
begin += interVal;
}
}
}else if(cron.contains("-")){
String[] split = cron.split("-");
int begin = Integer.parseInt(split[0]);
int end = Integer.parseInt(split[1]);
if(superFlag){
stringBuilder.append("的");
}else {
stringBuilder.append("每").append(superLevel).append("的");
}
for (int i = 0; begin <= end; i++){
if(i == 0){
stringBuilder.append(numToWeek(begin));
}else {
stringBuilder.append(",").append(numToWeek(begin));
}
begin++;
}
}else if(cron.contains(",")){
List<String> list = Arrays.asList(cron.split(","));
if(superFlag){
stringBuilder.append("的");
}else {
stringBuilder.append("每").append(superLevel).append("的");
}
for (int i = 0; i < list.size(); i++){
String num = list.get(i);
if(i == 0){
stringBuilder.append(num).append(levelShort);
}else {
stringBuilder.append(",").append(numToWeek(Integer.parseInt(num)));
}
}
}
//周
// L这一周的最后一天 5L 这个月的最后一个星期五
// # 6#3 第三个周五
else if(index == 5 && "L".equals(cron)){
if(superFlag){
stringBuilder.append("的").append("每个星期日");
}else {
stringBuilder.append("每").append(superLevel).append("每个星期日");
}
}
else if(index == 5 && cron.contains("L")){
String dayNear = cron.substring(0, cron.length() - 1);
if(superFlag){
stringBuilder.append("的").append("最后一个").append(numToWeek(Integer.parseInt(dayNear)));
}else {
stringBuilder.append("每").append(superLevel).append("的最后一个").append(numToWeek(Integer.parseInt(dayNear)));
}
}
else if(index == 5 && cron.contains("#")){
String[] split = cron.split("#");
String weekNum = split[0];
String weekOrder = split[1];
String dayNear = cron.substring(0, cron.length() - 1);
if(superFlag){
stringBuilder.append("的").append("第").append(weekOrder).append("个").append(numToWeek(Integer.parseInt(weekNum)));
}else {
stringBuilder.append("每").append(superLevel).append("第").append(weekOrder).append("个").append(numToWeek(Integer.parseInt(weekNum)));
}
}
else {
if(superFlag){
stringBuilder.append("的").append("每个").append(numToWeek(Integer.parseInt(cron)));
}else {
stringBuilder.append("每个").append(numToWeek(Integer.parseInt(cron)));
}
}
return stringBuilder;
}else {
return null;
}
}
private static String numToWeek(Integer num){
switch (num){
case 1:
return "星期日";
case 2:
return "星期一";
case 3:
return "星期二";
case 4:
return "星期三";
case 5:
return "星期四";
case 6:
return "星期五";
case 7:
return "星期六";
default:
break;
}
return "";
}
调用效果如下
public static void main(String[] args) {
String s = cronParse("0 0/1 11,13 * 1/1 ?");
System.out.println(s);
}
每月每日的11时,13时每分的0秒