feat: support DailyTimeInterval #558

This commit is contained in:
tjq 2023-02-11 22:57:43 +08:00
parent 9f2f68344c
commit 42823b8bdd
4 changed files with 222 additions and 3 deletions

View File

@ -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();

View File

@ -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转为熟悉的 112
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 {

View File

@ -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;
}
}

View File

@ -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);
// 时间未到当天 230000
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;
}
}