优化了本地定时器+下次的判断

This commit is contained in:
Yun
2025-09-14 19:05:10 +08:00
parent c351cb084f
commit 464b467868
10 changed files with 1662 additions and 209 deletions
+189 -62
View File
@@ -7,110 +7,237 @@ import (
// 计算该任务下次执行时间
// @param job *JobData 任务数据
// @param t time.Time 当前时间
// @return time.Time 下次执行时间
// @return error 错误信息
func GetNextTime(t time.Time, job JobData) (*time.Time, error) {
var next time.Time
if err := validateJobData(job); err != nil {
return nil, err
}
var next *time.Time
var err error
switch job.JobType {
case JobTypeEveryMonth:
next = calculateNextMonthTime(t, job)
next, err = calculateNextMonthTime(t, job)
case JobTypeEveryWeek:
next = calculateNextWeekTime(t, job)
next, err = calculateNextWeekTime(t, job)
case JobTypeEveryDay:
next = calculateNextDayTime(t, job)
next, err = calculateNextDayTime(t, job)
case JobTypeEveryHour:
next = calculateNextHourTime(t, job)
next, err = calculateNextHourTime(t, job)
case JobTypeEveryMinute:
next = calculateNextMinuteTime(t, job)
next, err = calculateNextMinuteTime(t, job)
case JobTypeInterval:
next = calculateNextInterval(t, job)
next, err = calculateNextInterval(t, job)
default:
return nil, errors.New("未知的任务类型: " + string(job.JobType))
}
if err != nil {
return nil, err
}
return next, nil
}
// 参数校验
func validateJobData(job JobData) error {
switch job.JobType {
case JobTypeEveryMonth:
if job.Day < 1 || job.Day > 31 {
return ErrMonthDay
}
case JobTypeEveryWeek:
if job.Weekday < time.Sunday || job.Weekday > time.Saturday {
return ErrWeekday
}
case JobTypeEveryDay:
if job.Hour < 0 || job.Hour > 23 {
return ErrHour
}
case JobTypeEveryHour:
if job.Minute < 0 || job.Minute > 59 {
return ErrMinute
}
case JobTypeEveryMinute:
if job.Second < 0 || job.Second > 59 {
return ErrSecond
}
case JobTypeInterval:
if job.IntervalTime <= 0 {
return ErrIntervalTime
}
if job.CreateTime.IsZero() {
return ErrCreateTime
}
}
if job.Hour < 0 || job.Hour > 23 {
return ErrHour
}
if job.Minute < 0 || job.Minute > 59 {
return ErrMinute
}
if job.Second < 0 || job.Second > 59 {
return ErrSecond
}
return nil
}
func calculateNextInterval(t time.Time, job JobData) (*time.Time, error) {
if job.CreateTime.IsZero() {
return nil, ErrCreateTime
}
if job.IntervalTime <= 0 {
return nil, ErrIntervalTime
}
// 计算从创建时间到当前时间经过了多少个间隔
elapsed := t.Sub(job.CreateTime)
intervals := elapsed / job.IntervalTime
// 计算下一个执行时间
next := job.CreateTime.Add((intervals + 1) * job.IntervalTime)
// 确保下次执行时间不早于当前时间
if next.Before(t) || next.Equal(t) {
next = next.Add(job.IntervalTime)
}
return &next, nil
}
func calculateNextInterval(t time.Time, job JobData) time.Time {
// 从创建的时候开始计算
cycle := t.Sub(job.BaseTime).Microseconds() / job.IntervalTime.Microseconds()
return job.BaseTime.Add(job.IntervalTime * time.Duration(cycle+1))
func calculateNextMonthTime(t time.Time, job JobData) (*time.Time, error) {
// 尝试光剑本月的执行四件
currentMonthTime := time.Date(t.Year(), t.Month(), job.Day, job.Hour, job.Minute, job.Second, 0, t.Location())
// 如果日期无效(比如2月30号),则调整到该月最后一天
if currentMonthTime.Day() != job.Day {
// 获取该月的最后一天
lastDay := time.Date(t.Year(), t.Month()+1, 0, 0, 0, 0, 0, t.Location()).Day()
if job.Day > lastDay {
currentMonthTime = time.Date(t.Year(), t.Month(), lastDay, job.Hour, job.Minute, job.Second, 0, t.Location())
}
}
if currentMonthTime.After(t) {
return &currentMonthTime, nil
}
// 计算下个月的同一天
nextMonth := t.Month() + 1
year := t.Year()
if nextMonth > 12 {
nextMonth = 1
year++
}
nextMonthTime := time.Date(year, nextMonth, job.Day, job.Hour, job.Minute, job.Second, 0, t.Location())
// 如果日期无效,调整到下个月的最后一天
if nextMonthTime.Day() != job.Day {
lastDay := time.Date(year, nextMonth+1, 0, 0, 0, 0, 0, t.Location()).Day()
if job.Day > lastDay {
nextMonthTime = time.Date(year, nextMonth, lastDay, job.Hour, job.Minute, job.Second, 0, t.Location())
}
}
return &nextMonthTime, nil
}
func calculateNextMonthTime(t time.Time, job JobData) time.Time {
// 判断是否可执行并返回下一个执行时间
func calculateNextWeekTime(t time.Time, job JobData) (*time.Time, error) {
currentWeekday := t.Weekday()
targetWeekday := job.Weekday
if canRun(t, job) {
return time.Date(t.Year(), t.Month(), job.Day, job.Hour, job.Minute, job.Second, 0, t.Location())
// 计算距离目标星期几的天数
daysToAdd := int(targetWeekday - currentWeekday)
if daysToAdd < 0 {
daysToAdd += 7
}
// 下一个周期(下个月)
return time.Date(t.Year(), t.Month()+1, job.Day, job.Hour, job.Minute, job.Second, 0, t.Location())
// 本周的目标时间
thisWeekTime := time.Date(t.Year(), t.Month(), t.Day()+daysToAdd, job.Hour, job.Minute, job.Second, 0, t.Location())
if thisWeekTime.After(t) {
return &thisWeekTime, nil
}
// 下周的目标时间
nextWeekTime := time.Date(t.Year(), t.Month(), t.Day()+daysToAdd+7, job.Hour, job.Minute, job.Second, 0, t.Location())
return &nextWeekTime, nil
}
func calculateNextWeekTime(t time.Time, job JobData) time.Time {
weekday := t.Weekday()
days := int(job.Weekday - weekday)
if days < 0 {
days += 7
func calculateNextDayTime(t time.Time, job JobData) (*time.Time, error) {
// 今天的目标时间
todayTime := time.Date(t.Year(), t.Month(), t.Day(), job.Hour, job.Minute, job.Second, 0, t.Location())
if todayTime.After(t) {
return &todayTime, nil
}
// 判断是否可执行并返回下一个执行时间
if canRun(t, job) {
return time.Date(t.Year(), t.Month(), t.Day(), job.Hour, job.Minute, job.Second, 0, t.Location())
}
// 下一个周期(下周)
return time.Date(t.Year(), t.Month(), t.Day()+days+7, job.Hour, job.Minute, job.Second, 0, t.Location())
// 明天的时间
nextDayTime := time.Date(t.Year(), t.Month(), t.Day()+1, job.Hour, job.Minute, job.Second, 0, t.Location())
return &nextDayTime, nil
}
func calculateNextDayTime(t time.Time, job JobData) time.Time {
// 判断是否可执行并返回下一个执行时间
if canRun(t, job) {
return time.Date(t.Year(), t.Month(), t.Day(), job.Hour, job.Minute, job.Second, 0, t.Location())
func calculateNextHourTime(t time.Time, job JobData) (*time.Time, error) {
// 计算当前小时的目标时间
currentHourTime := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), job.Minute, job.Second, 0, t.Location())
if currentHourTime.After(t) {
return &currentHourTime, nil
}
// 下一个周期(明天)
return time.Date(t.Year(), t.Month(), t.Day()+1, job.Hour, job.Minute, job.Second, 0, t.Location())
// 下一个小时的时间
nextHourTime := time.Date(t.Year(), t.Month(), t.Day(), t.Hour()+1, job.Minute, job.Second, 0, t.Location())
return &nextHourTime, nil
}
func calculateNextHourTime(t time.Time, job JobData) time.Time {
// 判断是否可执行并返回下一个执行时间
if canRun(t, job) {
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), job.Minute, job.Second, 0, t.Location())
}
// 下一个周期(下个小时)
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour()+1, job.Minute, job.Second, 0, t.Location())
}
func calculateNextMinuteTime(t time.Time, job JobData) (*time.Time, error) {
// 计算当前分钟的目标时间
func calculateNextMinuteTime(t time.Time, job JobData) time.Time {
// 判断是否可执行并返回下一个执行时间
if canRun(t, job) {
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), job.Second, 0, t.Location())
currentMinuteTime := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), job.Second, 0, t.Location())
if currentMinuteTime.After(t) {
return &currentMinuteTime, nil
}
// 下一个周期(下分钟)
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()+1, job.Second, 0, t.Location())
// 下一分钟的时间
nextMinuteTime := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()+1, job.Second, 0, t.Location())
return &nextMinuteTime, nil
}
// 检查是否本周期可以运行
// 检查是否本周期可以运行(已弃用,使用新的时间比较逻辑)
// 保留此函数用于向后兼容,但建议使用新的时间计算逻辑
func canRun(t time.Time, job JobData) bool {
targetTime := time.Date(t.Year(), t.Month(), t.Day(), job.Hour, job.Minute, job.Second, 0, t.Location())
switch job.JobType {
case JobTypeEveryMonth:
return t.Day() < job.Day ||
(t.Day() == job.Day && t.Hour() < job.Hour) ||
(t.Day() == job.Day && t.Hour() == job.Hour && t.Minute() < job.Minute) ||
(t.Day() == job.Day && t.Hour() == job.Hour && t.Minute() == job.Minute && t.Second() <= job.Second)
// 对于月任务,需要比较日期
targetTime = time.Date(t.Year(), t.Month(), job.Day, job.Hour, job.Minute, job.Second, 0, t.Location())
return !targetTime.Before(t)
case JobTypeEveryWeek:
return t.Weekday() < job.Weekday ||
(t.Weekday() == job.Weekday && t.Hour() < job.Hour) ||
(t.Weekday() == job.Weekday && t.Hour() == job.Hour && t.Minute() < job.Minute) ||
(t.Weekday() == job.Weekday && t.Hour() == job.Hour && t.Minute() == job.Minute && t.Second() <= job.Second)
// 对于周任务,需要比较星期
currentWeekday := t.Weekday()
if currentWeekday < job.Weekday {
return true
}
if currentWeekday == job.Weekday {
return targetTime.After(t) || targetTime.Equal(t)
}
return false
case JobTypeEveryDay:
return t.Hour() < job.Hour ||
(t.Hour() == job.Hour && t.Minute() < job.Minute) ||
(t.Hour() == job.Hour && t.Minute() == job.Minute && t.Second() <= job.Second)
return targetTime.After(t) || targetTime.Equal(t)
case JobTypeEveryHour:
return t.Minute() < job.Minute ||
(t.Minute() == job.Minute && t.Second() <= job.Second)
hourTarget := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), job.Minute, job.Second, 0, t.Location())
return hourTarget.After(t) || hourTarget.Equal(t)
case JobTypeEveryMinute:
return t.Second() <= job.Second
minuteTarget := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), job.Second, 0, t.Location())
return minuteTarget.After(t) || minuteTarget.Equal(t)
default:
return false
}