mirror of
https://github.com/PowerJob/PowerJob.git
synced 2025-07-17 00:00:04 +08:00
feat: support DailyTimeInterval #558
This commit is contained in:
parent
9f2f68344c
commit
42823b8bdd
@ -28,6 +28,37 @@ public class TimeUtils {
|
||||
*/
|
||||
private static final long MAX_OFFSET = 5000;
|
||||
|
||||
/**
|
||||
* 根据蔡勒公式计算任意一个日期是星期几
|
||||
* @param year 年
|
||||
* @param month 月
|
||||
* @param day 日
|
||||
* @return 中国星期
|
||||
*/
|
||||
public static int calculateWeek(int year, int month, int day) {
|
||||
if (month == 1) {
|
||||
month = 13;
|
||||
year--;
|
||||
}
|
||||
if (month == 2) {
|
||||
month = 14;
|
||||
year--;
|
||||
}
|
||||
int y = year % 100;
|
||||
int c = year /100 ;
|
||||
int h = (y + (y / 4) + (c / 4) - (2 * c) + ((26 * (month + 1)) / 10) + day - 1) % 7;
|
||||
//可能是负值,因此计算除以7的余数之后需要判断是大于等于0还是小于0,如果小于0则将余数加7。
|
||||
if (h < 0){
|
||||
h += 7;
|
||||
}
|
||||
|
||||
// 国内理解中星期日为 7
|
||||
if (h == 0) {
|
||||
return 7;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
public static void check() throws TimeCheckException {
|
||||
|
||||
NTPUDPClient timeClient = new NTPUDPClient();
|
||||
|
@ -1,20 +1,24 @@
|
||||
package tech.powerjob.server.core.scheduler.auxiliary.impl;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import tech.powerjob.common.OmsConstant;
|
||||
import tech.powerjob.common.enums.TimeExpressionType;
|
||||
import tech.powerjob.common.serialize.JsonUtils;
|
||||
import tech.powerjob.common.utils.CollectionUtils;
|
||||
import tech.powerjob.common.utils.CommonUtils;
|
||||
import tech.powerjob.server.common.utils.TimeUtils;
|
||||
import tech.powerjob.server.core.scheduler.auxiliary.TimeOfDay;
|
||||
import tech.powerjob.server.core.scheduler.auxiliary.TimingStrategyHandler;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* DailyTimeIntervalStrategyHandler
|
||||
@ -23,6 +27,11 @@ import java.util.Set;
|
||||
*/
|
||||
public class DailyTimeIntervalStrategyHandler implements TimingStrategyHandler {
|
||||
|
||||
/**
|
||||
* 使用中国人的星期!!!
|
||||
*/
|
||||
private static final Set<Integer> ALL_DAY = Sets.newHashSet(1, 2, 3, 4, 5, 6, 7);
|
||||
|
||||
@Override
|
||||
public TimeExpressionType supportType() {
|
||||
return TimeExpressionType.DAILY_TIME_INTERVAL;
|
||||
@ -41,14 +50,90 @@ public class DailyTimeIntervalStrategyHandler implements TimingStrategyHandler {
|
||||
if (endTime.before(startTime)) {
|
||||
throw new IllegalArgumentException("endTime should after startTime!");
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(ep.intervalUnit)) {
|
||||
TimeUnit.valueOf(ep.intervalUnit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public Long calculateNextTriggerTime(Long preTriggerTime, String timeExpression, Long startTime, Long endTime) {
|
||||
DailyTimeIntervalExpress ep = JsonUtils.parseObject(timeExpression, DailyTimeIntervalExpress.class);
|
||||
|
||||
// 未开始状态下,用起点算调度时间
|
||||
if (startTime != null && startTime > System.currentTimeMillis() && preTriggerTime < startTime) {
|
||||
return calculateInRangeTime(startTime, ep);
|
||||
}
|
||||
|
||||
// 间隔时间
|
||||
TimeUnit timeUnit = Optional.ofNullable(ep.intervalUnit).map(TimeUnit::valueOf).orElse(TimeUnit.SECONDS);
|
||||
long interval = timeUnit.toMillis(ep.interval);
|
||||
|
||||
Long ret = calculateInRangeTime(preTriggerTime + interval, ep);
|
||||
if (ret == null || ret <= endTime) {
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算最近一次在范围中的时间
|
||||
* @param time 当前时间基准,可能直接返回该时间作为结果
|
||||
* @param ep 表达式
|
||||
* @return 最近一次在范围中的时间
|
||||
*/
|
||||
static Long calculateInRangeTime(Long time, DailyTimeIntervalExpress ep) {
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(new Date(time));
|
||||
|
||||
int year = calendar.get(Calendar.YEAR);
|
||||
// 月份 + 1,转为熟悉的 1~12 月
|
||||
int month = calendar.get(Calendar.MONTH) + 1;
|
||||
int day = calendar.get(Calendar.DAY_OF_MONTH);
|
||||
|
||||
// 判断是否符合"日"的执行条件
|
||||
int week = TimeUtils.calculateWeek(year, month, day);
|
||||
Set<Integer> targetDays = CollectionUtils.isEmpty(ep.daysOfWeek) ? ALL_DAY : ep.daysOfWeek;
|
||||
// 未包含情况下,将时间改写为符合条件日的 00:00 分,重新开始 loop(这部分应该有性能更优的写法,不过这个调度模式应该很难触发瓶颈,先简单好用的实现)
|
||||
if (!targetDays.contains(week)) {
|
||||
simpleSetCalendar(calendar, 0, 0, 0);
|
||||
Date tomorrowZero = DateUtils.addDays(calendar.getTime(), 1);
|
||||
return calculateInRangeTime(tomorrowZero.getTime(), ep);
|
||||
}
|
||||
|
||||
// 范围的开始时间
|
||||
TimeOfDay rangeStartTime = TimeOfDay.from(ep.startTimeOfDay);
|
||||
simpleSetCalendar(calendar, rangeStartTime.getHour(), rangeStartTime.getMinute(), rangeStartTime.getSecond());
|
||||
long todayStartTs = calendar.getTimeInMillis();
|
||||
|
||||
TimeOfDay rangeEndTime = TimeOfDay.from(ep.endTimeOfDay);
|
||||
simpleSetCalendar(calendar, rangeEndTime.getHour(), rangeEndTime.getMinute(), rangeEndTime.getSecond());
|
||||
long todayEndTs = calendar.getTimeInMillis();
|
||||
|
||||
// 未开始
|
||||
if (time < todayStartTs) {
|
||||
return todayStartTs;
|
||||
}
|
||||
|
||||
// 范围之间
|
||||
if (time <= todayEndTs) {
|
||||
return time;
|
||||
}
|
||||
|
||||
// 已结束,重新计算第二天时间
|
||||
simpleSetCalendar(calendar, 0, 0, 0);
|
||||
return calculateInRangeTime(DateUtils.addDays(calendar.getTime(), 1).getTime(), ep);
|
||||
}
|
||||
|
||||
private static void simpleSetCalendar(Calendar calendar, int h, int m, int s) {
|
||||
calendar.set(Calendar.SECOND, s);
|
||||
calendar.set(Calendar.MINUTE, m);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, h);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
}
|
||||
|
||||
@Data
|
||||
static class DailyTimeIntervalExpress implements Serializable {
|
||||
|
||||
|
@ -0,0 +1,53 @@
|
||||
package tech.powerjob.server.common.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* 时间工具 Test
|
||||
*
|
||||
* @author tjq
|
||||
* @since 2020/5/19
|
||||
*/
|
||||
@Slf4j
|
||||
class TimeUtilsTest {
|
||||
|
||||
@Test
|
||||
void calculateWeek() {
|
||||
int weekOf20230211 = TimeUtils.calculateWeek(2023, 2, 11);
|
||||
log.info("[TimeUtilsTest] weekOf20230211: {}", weekOf20230211);
|
||||
assert weekOf20230211 == 6;
|
||||
|
||||
int weekOf20230212 = TimeUtils.calculateWeek(2023, 2, 12);
|
||||
log.info("[TimeUtilsTest] weekOf20230211: {}", weekOf20230212);
|
||||
assert weekOf20230212 == 7;
|
||||
|
||||
int weekOf20230401 = TimeUtils.calculateWeek(2023, 4, 1);
|
||||
log.info("[TimeUtilsTest] weekOf20230401: {}", weekOf20230401);
|
||||
assert weekOf20230401 == 6;
|
||||
|
||||
|
||||
// 618
|
||||
int weekOf20230618 = TimeUtils.calculateWeek(2023, 6, 18);
|
||||
log.info("[TimeUtilsTest] weekOf20230618: {}", weekOf20230618);
|
||||
assert weekOf20230618 == 7;
|
||||
|
||||
// 双十一
|
||||
int weekOf20231111 = TimeUtils.calculateWeek(2023, 11, 11);
|
||||
log.info("[TimeUtilsTest] weekOf20231111: {}", weekOf20231111);
|
||||
assert weekOf20231111 == 6;
|
||||
|
||||
// 我发现所有我熟悉的日子都是周末,神器啊....
|
||||
|
||||
int weekOf20230723 = TimeUtils.calculateWeek(2023, 7, 23);
|
||||
log.info("[TimeUtilsTest] weekOf20230723: {}", weekOf20230723);
|
||||
assert weekOf20230723 == 7;
|
||||
|
||||
int weekOf20230218 = TimeUtils.calculateWeek(2023, 2, 18);
|
||||
log.info("[TimeUtilsTest] weekOf20230218: {}", weekOf20230218);
|
||||
assert weekOf20230218 == 6;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package tech.powerjob.server.core.scheduler.auxiliary.impl;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* DailyTimeIntervalStrategyHandler
|
||||
* @author 550w
|
||||
* @date 2027/02/15
|
||||
*/
|
||||
@Slf4j
|
||||
class DailyTimeIntervalStrategyHandlerTest {
|
||||
|
||||
@Test
|
||||
void calculateInRangeTime() {
|
||||
|
||||
// 2023-02-11 22:46:34
|
||||
long ts = 1676126794874L;
|
||||
// 验证范围中
|
||||
Long nowTs = DailyTimeIntervalStrategyHandler.calculateInRangeTime(ts, simpleBuild("21:00:00", "23:00:00", null));
|
||||
assert nowTs.equals(ts);
|
||||
|
||||
// 时间未到(当天 23:00:00)
|
||||
Long nextTs = DailyTimeIntervalStrategyHandler.calculateInRangeTime(ts, simpleBuild("23:00:00", "23:15:00", null));
|
||||
assert nextTs.equals(1676127600000L);
|
||||
|
||||
// 时间超出(第二天 11:00:00)
|
||||
Long nextDayStartTs = DailyTimeIntervalStrategyHandler.calculateInRangeTime(ts, simpleBuild("11:00:00", "12:15:00", null));
|
||||
assert nextDayStartTs.equals(1676170800000L);
|
||||
|
||||
// 星期不满足(2023-02-15 11:00:00)
|
||||
Long notTodayStartTs = DailyTimeIntervalStrategyHandler.calculateInRangeTime(ts, simpleBuild("11:00:00", "12:15:00", Sets.newHashSet(3)));
|
||||
assert notTodayStartTs.equals(1676430000000L);
|
||||
|
||||
}
|
||||
|
||||
private static DailyTimeIntervalStrategyHandler.DailyTimeIntervalExpress simpleBuild(String startTimeOfDay, String endTimeOfDay, Set<Integer> days) {
|
||||
DailyTimeIntervalStrategyHandler.DailyTimeIntervalExpress ep = new DailyTimeIntervalStrategyHandler.DailyTimeIntervalExpress();
|
||||
ep.setInterval(30L);
|
||||
ep.setStartTimeOfDay(startTimeOfDay);
|
||||
ep.setEndTimeOfDay(endTimeOfDay);
|
||||
ep.setDaysOfWeek(days);
|
||||
return ep;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user