前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Goland实现类Crond时间规则算法

Goland实现类Crond时间规则算法

作者头像
王宝
修改2024-11-23 10:57:52
修改2024-11-23 10:57:52
780
举报

场景:

管理人新建任务,并指定该任务的执行crond规则

逻辑:

通过Go程序启动一个每秒执行的定时器,监听创建好的任务列表,通过当前时间进行一定的转换,判断转换后的数据是否落在对应crond范围内

实现:

以下代码主要实现,当前时间是否满足cron表达的要求

代码语言:txt
复制
package cronpackage

import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"
)

type CheckCrontab struct{}

func NewCheckCrontab() *CheckCrontab {
	return &CheckCrontab{}
}

/**
 * 此类仅用于判断给定的时间是否符合时间规则,如果符合则返回true其它业务逻辑自行处理
 * 使用说明
 *  format = '* * * * * ';
 * 本类模仿crond的规则分5个部分分别是分、时、日期、月、周(的第几天)
 * 每部分可单独设置为一个数,如:1 * * * * ,当分为1时 true
 * 可以使用 '/' 来分开设置重复规则,如: * / 1 * * * * , 每分钟 ; * / 2 * * * * 每两分钟(注意每部分之间是没有空格的,这里是PHP的注释部分)
 * 可以设置区间: 4-20/2 * * * * , 当分为4 - 20之间,并且为2的倍数时
 * 可以为集合: * /5,8,10 * * * * , 当分为5,8,10时返回true,其它部分同理。
 * '0 23 * * 6' 每周六的11点
 * '* 23-7/1 * * * ' 每天晚上23-7点,每隔一小时
 */

func (c *CheckCrontab) Handle(ctx context.Context) {
	currentTime := time.Now()
	_, err := NewCheckCrontab().Check(currentTime, "'* * * * *")
	fmt.Println(err)
}

func (c *CheckCrontab) Check(t time.Time, strCron string) (bool, error) {

	formatTime := strings.Split(t.Format("04 15 02 01"), " ")                //分钟、小时、日、月
	formatTime = append(formatTime, strconv.Itoa(int(time.Now().Weekday()))) //星期几

	formatCron, err := c.FormatCrontab(strCron)
	if err != nil {
		return false, err
	}

	defaultRes := false
	for i, v := range formatTime {
		vInt, err := strconv.Atoi(v)
		if err != nil {
			return false, err
		}
		if len(formatCron[i]) > 0 && !InArray(vInt, formatCron[i]) { //此处不满足则直接返回
			return false, nil
		}
		defaultRes = true
	}

	return defaultRes, nil
}

func (c *CheckCrontab) FormatCrontab(strCron string) ([][]int, error) {
	var arrCron [][]int

	_, err := cron.ParseStandard(strCron) //校验表达式
	if err != nil {
		return arrCron, err
	}

	parts := strings.Split(strCron, " ")
	part0, _ := c.ParseCronPart(parts[0], 0, 59)
	arrCron = append(arrCron, part0) //分

	part1, _ := c.ParseCronPart(parts[1], 0, 59)
	arrCron = append(arrCron, part1) //时

	part2, _ := c.ParseCronPart(parts[2], 1, 31)
	arrCron = append(arrCron, part2) //日

	part3, _ := c.ParseCronPart(parts[3], 1, 12)
	arrCron = append(arrCron, part3) //月

	part4, _ := c.ParseCronPart(parts[4], 0, 6)
	arrCron = append(arrCron, part4) //周(0周日)

	return arrCron, nil
}

// part 时间计划里的一个部分,被空格分隔后的一个部分
// fMin 此部分的最小取值
// fMax 此部分的最大取值
func (c *CheckCrontab) ParseCronPart(part string, fMin int, fMax int) ([]int, error) {
	list := []int{}

	//处理"," -- 列表
	if strings.Contains(part, ",") {
		arr := strings.Split(part, ",")
		for _, v := range arr {
			tmpList, _ := c.ParseCronPart(v, fMin, fMax)
			list = append(list, tmpList...)
		}
		return list, nil
	}

	//处理"/" -- 间隔
	tmp := strings.Split(part, "/")
	part = tmp[0]
	step := 1
	if len(tmp) > 1 && tmp[1] != "" {
		tmp1, _ := strconv.Atoi(tmp[1])
		step = tmp1
	}

	//处理"-" -- 范围
	min := 0
	max := 0
	if strings.Contains(part, "-") {
		var err error

		partArr := strings.Split(part, "-")
		if len(partArr) != 2 {
			return list, errors.New("Parameter exception when using '-' to set the range")
		}
		min, err = strconv.Atoi(partArr[0])
		if err != nil {
			return list, errors.New("The minimum value is not a number")
		}

		max, err = strconv.Atoi(partArr[1])
		if err != nil {
			return list, errors.New("The maximum value is not a number")
		}

		if min > max {

			return list, errors.New("When using '-' to set the range, the left cannot be greater than the right")
		}
	} else if part == "*" {
		min = fMin
		max = fMax
	} else { //数字
		partNum, _ := strconv.Atoi(part)
		min = partNum
		max = partNum
	}

	//空数组表示可以任意值
	if min == fMin && max == fMax && step == 1 {
		return list, nil
	}

	//越界判断
	if min < fMin || max > fMax {

		return list, errors.New("The value is out of range. Should be: minutes 0-59, hours 0-59, days 1-31, months 1-12, weeks 0-6")
	}

	if max-min > step {
		return c.rangeFunction(min, max, step), nil
	}
	return []int{min}, nil
}

func (c *CheckCrontab) rangeFunction(start, end, step int) []int {
	var result []int

	if step == 0 {
		return result
	}

	for start != end {
		result = append(result, start)
		start += step

		if start == end {
			result = append(result, start)
			break
		}
		if start > end {
			break
		}
	}

	return result
}

// 判断某一个值是否含在切片之中
func InArray(need interface{}, haystack interface{}) bool {
	switch key := need.(type) {
	case int:
		for _, item := range haystack.([]int) {
			if item == key {
				return true
			}
		}
	case string:
		for _, item := range haystack.([]string) {
			if item == key {
				return true
			}
		}
	case int64:
		for _, item := range haystack.([]int64) {
			if item == key {
				return true
			}
		}
	case float64:
		for _, item := range haystack.([]float64) {
			if item == key {
				return true
			}
		}
	default:
		return false
	}
	return false
}

参考文档:https://blog.csdn.net/hotlinhao/article/details/79341443

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 场景:
  • 逻辑:
  • 实现:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档