目录
前言
最近写任务遇到个算时间的需求,在此记录一下。根据时间跳过法定节假日算出+1天的时间,根据时间即跳过法定节假日,又跳过工作日休息时间+n小时,算出时间。
一、导入每年的法定节假日(我这里入库)
@Slf4j
@Component
public class StatutoryHolidaysTask {
@Resource
private EventService eventService;
@PostConstruct
public void initializeHolidays() {
initializeHolidays(null);
}
public void initializeHolidays(LocalDate beginTime) {
List<StatutoryHolidays> list = statutoryHolidaysMapper.list(beginTime, null);
if (list == null || list.isEmpty()) {
syncHoliday();
}
DateUtil.clearHolidayList();
Map<LocalDate, Integer> date = list.stream().collect(Collectors.toMap(StatutoryHolidays::getHolidaysDate, StatutoryHolidays::getHoliday));
DateUtil.setHolidayList(date);
}
@Resource
private StatutoryHolidaysMapper statutoryHolidaysMapper;
private static String SYNC_ADDRESS = "https://timor.tech/api/holiday/year/";
//# 法定节假日定时任务配置
//syncHolidayDeadline: 0 5 1 28 12 *
@Scheduled(cron = "${syncHolidayDeadline}")
public void syncHoliday() {
syncHoliday(null);
}
public void syncHoliday(LocalDate date) {
log.error("StatutoryHolidaysTask.syncHoliday begin ==== {}", date);
String url = SYNC_ADDRESS + (date == null ? LocalDate.now().getYear() : date.getYear());
String result = null;
try {
result = HttpClientUtil.get(url, null);
} catch (Exception e) {
log.error("StatutoryHolidaysTask.syncHoliday param:{} error:", url, e);
}
if (StringUtils.isBlank(result)) {
log.error("StatutoryHolidaysTask.syncHoliday result 为空");
return;
}
Map map = JSONObject.parseObject(result, Map.class);
Integer code = (Integer) map.get("code");
if (code == 0) {
JSONObject holidayJson = (JSONObject) map.get("holiday");
List<HolidayVo> rawInfoList = new ArrayList<>();
holidayJson.forEach((k, v) -> {
if (v == null) {
return;
}
cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(v);
HolidayVo holidayRawInfo = JSONUtil.toBean(jsonObject, HolidayVo.class);
rawInfoList.add(holidayRawInfo);
});
// 定义节假日集合
if (rawInfoList == null || rawInfoList.isEmpty()) {
log.error("StatutoryHolidaysTask.syncHoliday 节假日为空");
return;
}
List<LocalDate> hasHolidays = new ArrayList<>();
List<StatutoryHolidays> holidaysList = rawInfoList.stream().map(item -> {
StatutoryHolidays temp = new StatutoryHolidays();
temp.setHolidaysDate(LocalDate.parse(item.getDate()));
temp.setHolidaysYear(temp.getHolidaysDate().getYear());
temp.setHolidaysMonth(temp.getHolidaysDate().getMonthValue());
temp.setHolidaysDay(temp.getHolidaysDate().getDayOfMonth());
temp.setHolidaysName(item.getName());
temp.setHoliday(Boolean.TRUE.equals(item.getHoliday()) ?
CommonConstant.CommonStatusEnum.YES.getCode() : CommonConstant.CommonStatusEnum.NO.getCode());
hasHolidays.add(temp.getHolidaysDate());
return temp;
}).sorted(Comparator.comparing(StatutoryHolidays::getHolidaysDate)).collect(Collectors.toList());
List<LocalDate> has = CollectionUtils.isEmpty(hasHolidays) ? new ArrayList<>() :
statutoryHolidaysMapper.selectHasHolidays(hasHolidays);
if (!CollectionUtils.isEmpty(has)) {
holidaysList = holidaysList.stream().filter(item -> !has.contains(item.getHolidaysDate())).collect(Collectors.toList());
}
if (!CollectionUtils.isEmpty(holidaysList)) {
statutoryHolidaysMapper.insertBatch(holidaysList);
}
}
log.error("StatutoryHolidaysTask.syncHoliday end ==== ");
}
}
二、创建工具类
1.创建map(用来存放法定节假日)
代码如下(示例):
public class DateUtil {
private static Map<LocalDate, Integer> HOLIDAY_LIST = new HashMap<>();
public static void setHolidayList(Map<LocalDate, Integer> holidayList) {
HOLIDAY_LIST = holidayList;
}
}
2.根据传入时间算出跳过法定节假日的时间
代码如下(示例):
public class DateUtil {
private static Map<LocalDate, Integer> HOLIDAY_LIST = new HashMap<>();
public static void setHolidayList(Map<LocalDate, Integer> holidayList) {
HOLIDAY_LIST = holidayList;
}
/**
* 获取 传入参数 num个工作日后日期
*
* @param day 不能为空
* @param offSetNum 正整数 为之后 负整数为之前
* @return 获取 传入参数 num个工作日后日期 参数为空 返回当前时间
*/
public static Date getDaysAfterOffSetTime(Date day, Integer offSetNum) {
if (day == null || offSetNum == null) {
return new Date();
}
Boolean positiveNumber = NumberUtils.INTEGER_ZERO > offSetNum;
Date today = day;
Date tomorrow = null;
int delay = 1;
if (positiveNumber) {
offSetNum = Math.abs(offSetNum);
}
//根据需要设置,这个值就是业务需要的n个工作日
int num = offSetNum;
while (delay <= num) {
if (positiveNumber) {
tomorrow = getYesterday(today);
} else {
tomorrow = getTomorrow(today);
}
//当前日期+1 即tomorrow,判断是否是周末,都不是则将scheduleActiveDate日期+1,直到循环num次即可
// 是假期直接跳过; 不是假期: 1.周末,跳过, 2.不是周末,不跳过
if (!checkHoliday(tomorrow)) {
delay++;
}
today = tomorrow;
}
return getEndOfDay(today);
}
/**
* 入参为空 和 不是假期返回 false, 是假期返回 true
* @param tomorrow
* @return
*/
private static boolean checkHoliday(Date tomorrow) {
if (tomorrow == null) {
return false;
}
LocalDate localDate = tomorrow.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
Integer holiday = HOLIDAY_LIST.get(localDate);
if (holiday == null) {
// 没有匹配到(不是假期) -> 看周末
return isWeekend(getFormatDate(tomorrow, DATE_FORMAT_YYYY_MM_DD));
}
return CommonConstant.CommonStatusEnum.YES.getCode().equals(holiday);
}
/**
* 获取明天的日期
*
* @param date
* @return
*/
public static Date getTomorrow(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH, +1);
date = calendar.getTime();
return date;
}
/**
* 获取昨天的日期
*
* @param date
* @return
*/
public static Date getYesterday(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH, -1);
date = calendar.getTime();
return date;
}
/**
* 获取一天结束时间
* @param date
* @return
*/
public static Date getEndOfDay(Date date) {
Calendar calendarEnd = Calendar.getInstance();
calendarEnd.setTime(date);
return calendarEnd.getTime();
}
}
3.根据传入时间算出跳过法定节假日和工作日休息时间的时间
代码如下(示例):
public class DateUtil {
private static Map<LocalDate, Integer> HOLIDAY_LIST = new HashMap<>();
public static void setHolidayList(Map<LocalDate, Integer> holidayList) {
HOLIDAY_LIST = holidayList;
}
//这些变量需要设置值
public static final String WORK_HOURS_START = " 09:00:00";//上班时间
public static final String WORK_HOURS_END = " 17:00:00";//下班时间
public static final String NOON_BREAK_START = " 11:30:00";//午休开始时间
public static final String NOON_BREAK_END = " 14:00:00";//午休结束时间
/**
* 根据时间 + n小时 算出跳过法定节假日和作息时间
* @param date 时间
* @param hour 几小时
* @return
*/
@SneakyThrows
public static Date getDaysAfterHourSetTime(Date date,Integer hour) {
Date current = date;
//指定时间 1小时 单位小时 ,自己根据需求修正
int zdSec = hour * 60 * 60;
SimpleDateFormat hm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat hm6 = new SimpleDateFormat("yyyy-MM-dd");
// TimeDifference morningWorkTime = getDatePoor(hm.parse(startWork), hm.parse(lunchBreak));
// TimeDifference afternoonWorkTime = getDatePoor(hm.parse(workGoOn), hm.parse(offDuty));
while (true) {
String startWork = hm6.format(current) + WORK_HOURS_START;
String lunchBreak = hm6.format(current) + NOON_BREAK_START;
String workGoOn = hm6.format(current) + NOON_BREAK_END;
String offDuty = hm6.format(current) + WORK_HOURS_END;
//今天是否为节假日
if (checkHoliday(current)) {
//是节假日
String sw = hm6.format(current) + WORK_HOURS_START;
current = getTomorrow(hm.parse(sw));
} else {
if (zdSec <= 0) {
break;
}
//如果当前时间小于 早上上班时间 把时间初始化到九点半
if (current.getTime() < hm.parse(startWork).getTime()) {
current = hm.parse(startWork);
continue;
}
//如果时间在上午上班内 就用当前时间 到上午下班时间减掉 是否大于有效时间
//如果不大于有效时间就直接九点半加上有效时间 如果大于有限时间 就减掉有效时间
//设置当前时间为下午上班时间
if (hm.parse(startWork).getTime() <= current.getTime() && current.getTime()
<= hm.parse(lunchBreak).getTime()) {
TimeDifference datePoor = getDatePoor(current, hm.parse(lunchBreak));
if (datePoor.getTotalSec() >= zdSec) {
current = addHour(zdSec, current, 2);
break;
} else {
zdSec = zdSec - datePoor.getTotalSec().intValue();
current = hm.parse(workGoOn);
continue;
}
}
if (hm.parse(lunchBreak).getTime() < current.getTime() && current.getTime()
< hm.parse(workGoOn).getTime()) {
//如果时间在午休时候 就让时间变成下午起始时间
current = hm.parse(workGoOn);
}
//如果时间在下午上班时间 就用当前时间 到下午下班时间减掉有效时间
if (hm.parse(workGoOn).getTime() <= current.getTime() && current.getTime()
<= hm.parse(offDuty).getTime()) {
TimeDifference datePoor = getDatePoor(current, hm.parse(offDuty));
if (datePoor.getTotalSec() >= zdSec) {
current = addHour(zdSec, current, 2);
break;
} else {
zdSec = zdSec - datePoor.getTotalSec().intValue();
current = getTomorrow(hm.parse(startWork));
continue;
}
}
//如果时间在下午下班时候 就跳过再次进入循环
if (current.getTime() > hm.parse(offDuty).getTime()) {
current = getTomorrow(hm.parse(startWork));
continue;
}
}
}
System.out.println("计算时间结果" + hm.format(current));
return current;
}
/**
* 计算两个时间相差天 时 分 秒 总秒数
* @param endDate
* @param nowDate
* @return
*/
public static TimeDifference getDatePoor(Date endDate, Date nowDate) {
long nd = 1000 * 24 * 60 * 60;
long nh = 1000 * 60 * 60;
long nm = 1000 * 60;
long ns = 1000;
// 获得两个时间的毫秒时间差异
long diff = nowDate.getTime()-endDate.getTime() ;
// 计算差多少天
long day = diff / nd;
// 计算差多少小时
long hour = diff % nd / nh;
// 计算差多少分钟
long min = diff % nd % nh / nm;
// 计算差多少秒//输出结果
long sec = diff % nd % nh % nm / ns;
Long totalsec=hour*60*60+min*60+sec;
return new TimeDifference(day, hour, min,sec,totalsec);
}
public static Date addHour(int time, Date creatTime, int type) {
//设置生效时间为一小时后
Calendar cal = Calendar.getInstance();
cal.setTime(creatTime);
switch (type) {
case 1: {
cal.add(Calendar.HOUR, time);// 24小时制
}
case 3: {
cal.add(Calendar.MINUTE, time);// 24小时制
}
case 2:{
cal.add(Calendar.SECOND, time);
}
}
Date effectivetime = cal.getTime();
return effectivetime;
}
/**
* 入参为空 和 不是假期返回 false, 是假期返回 true
* @param tomorrow
* @return
*/
private static boolean checkHoliday(Date tomorrow) {
if (tomorrow == null) {
return false;
}
LocalDate localDate = tomorrow.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
Integer holiday = HOLIDAY_LIST.get(localDate);
if (holiday == null) {
// 没有匹配到(不是假期) -> 看周末
return isWeekend(getFormatDate(tomorrow, DATE_FORMAT_YYYY_MM_DD));
}
return CommonConstant.CommonStatusEnum.YES.getCode().equals(holiday);
}
/**
* 获取明天的日期
*
* @param date
* @return
*/
public static Date getTomorrow(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH, +1);
date = calendar.getTime();
return date;
}
public static final String DATE_FORMAT_YYYY_MM_DD = "yyyy-MM-dd";
/**
* 判断是否是weekend
*
* @param sdate yyyy-MM-dd
* @return
*/
public static boolean isWeekend(String sdate) {
Date date = getFormatDate(sdate, DATE_FORMAT_YYYY_MM_DD);
Calendar cal = Calendar.getInstance();
cal.setTime(date);
if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
return true;
} else {
return false;
}
}
public static Date getFormatDate(String dateStr, String template) {
if (StringUtils.isBlank(dateStr)) {
return null;
}
try {
SimpleDateFormat destsmf = new SimpleDateFormat(template);
return destsmf.parse(dateStr);
} catch (Exception e) {
logger.error("格式化日期出错", e);
}
return null;
}
}
总结
以上就是我遇到的计算时间方式,在此记录一下。