目的:用来计算两个日期之间的工作时长
说明:其中节假日、工作日工作时间需要从配置表中获取
直接上代码
public static void main(String[] args) throws Exception {
calTime("2023-09-23 07:30:00", "2023-09-25 18:30:05");
}
/**
* 计算两个日期间的工作时长
*
* @param begintime yyyy-MM-dd HH:mm:ss
* @param endTime yyyy-MM-dd HH:mm:ss
* @return 工作时长/毫秒
*/
public static long calTime(String begintime, String endTime) {
Date startDate = DateUtil.parse(begintime);//节点到达时间
Date endDate = DateUtil.parse(endTime);//当前时间
int compareNum = DateUtil.compare(startDate, endDate);
if (compareNum > 0) {
String temp = endTime;
endTime = begintime;
begintime = temp;
}
startDate = DateUtil.parse(begintime);//节点到达时间
endDate = DateUtil.parse(endTime);//当前时间
System.out.println("计算前-hours-" + (((DateUtil.between(startDate, endDate, DateUnit.MS) * 1.0 / 3600000) - 3) > 0));
long millis = DateUtil.between(startDate, endDate, DateUnit.MS);
System.out.println("计算前-millis-" + millis);
System.out.println("计算前-" + DateUtil.formatBetween(millis, BetweenFormater.Level.SECOND));
//开始日期当天
String beginDateStr = DateUtil.format(DateUtil.parse(begintime), "yyyy-MM-dd");
String beginTimeStr = DateUtil.format(DateUtil.parse(begintime), "HH:mm:ss");
//截止日期当天
String endDateStr = DateUtil.format(DateUtil.parse(endTime), "yyyy-MM-dd");
String endTimeStr = DateUtil.format(DateUtil.parse(endTime), "HH:mm:ss");
long subMills = 0;//需要减去的时长
Map<String, Map<String, String>> workDayInfoMap = initWorkdayInfo();
// 同一天
if (beginDateStr.equals(endDateStr)) {
if (isWorkingDay(beginDateStr)) {
//工作日
long tempMills = calWorkDayRestMills(workDayInfoMap, beginDateStr, beginTimeStr, endTimeStr);
subMills += tempMills;
} else {
subMills = millis;//同一天且非工作日
}
} else {
long daysLen = DateUtil.between(DateUtil.parse(beginDateStr), DateUtil.parse(endDateStr), DateUnit.DAY, false);//相差天数
//处理开始日期
if (isWorkingDay(beginDateStr)) {
long tempMills = calWorkDayRestMills(workDayInfoMap, beginDateStr, beginTimeStr, "23:59:59") + 1000;
subMills += tempMills;
} else {
long tempMills = DateUtil.between(DateUtil.parse(begintime), DateUtil.parse(beginDateStr + " 23:59:59"), DateUnit.MS) + 1000;
subMills += tempMills;
}
//处理中间日期
if (daysLen > 1) {
for (int i = 1; i < daysLen; i++) {
String tempDateStr = DateUtil.format(DateUtil.offsetDay(DateUtil.parse(begintime), i), "yyyy-MM-dd");
if (isWorkingDay(tempDateStr)) {
long tempMills = calWorkDayRestMills(workDayInfoMap, tempDateStr, "00:00:00", "23:59:59") + 1000;
subMills += tempMills;
} else {
long tempMills = DateUtil.between(DateUtil.parse(tempDateStr + " 00:00:00")
, DateUtil.parse(tempDateStr + " 23:59:59"), DateUnit.MS) + 1000;
subMills += tempMills;
}
}
}
//处理截止日期
if (isWorkingDay(endDateStr)) {
long tempMills = calWorkDayRestMills(workDayInfoMap, endDateStr, "00:00:00", endTimeStr);//工时长
subMills += tempMills;
} else {
long tempMills = DateUtil.between(DateUtil.parse(endDateStr + " 00:00:00"), DateUtil.parse(endTime), DateUnit.MS);
subMills += tempMills;
}
}
System.out.println("计算后-subMills-" + subMills);
millis = millis - subMills;
System.out.println("计算后-millis-" + millis);
System.out.println("计算后-" + DateUtil.formatBetween(millis, BetweenFormater.Level.SECOND));
return millis;
}
/**
* 计算工作日,开始时间--截止时间知之间的非工作日时间长【日期是同一天,且是工作日】
*
* @param workDayInfoMap 工作日工作时间信息
* @param dateStr yyyy-MM-dd
* @param beginTimeStr HH:mm:ss
* @param endTimeStr HH:mm:ss
* @return
*/
private static Long calWorkDayRestMills(Map<String, Map<String, String>> workDayInfoMap, String dateStr, String beginTimeStr, String endTimeStr) {
if (workDayInfoMap == null || workDayInfoMap.size() == 0
|| dateStr == null || beginTimeStr == null || endTimeStr == null) {
return null;
}
//step1:判断时间大小
Integer hourB = DateUtil.hour(DateUtil.parse(beginTimeStr), true);
Integer hourE = DateUtil.hour(DateUtil.parse(endTimeStr), true);
if (hourB > hourE) {
String temp = endTimeStr;
endTimeStr = beginTimeStr;
beginTimeStr = temp;
}
//step2:获取两个时间的时间差,单位毫秒
long allMills = DateUtil.between(DateUtil.parse(dateStr + " " + beginTimeStr)
, DateUtil.parse(dateStr + " " + endTimeStr), DateUnit.MS);
System.out.println("计算中-allMills-" + allMills);
long workMills1 = 0;
long workMills2 = 0;
//step3:获取月份及工作日信息
Integer month = 1 + DateUtil.month(DateUtil.parse(dateStr));//DateUtil.month从0开始算
String monthStr = String.valueOf(month);
if (month < 10) {
monthStr = "0" + month;
}
Map<String, String> workInfo = workDayInfoMap.get(monthStr);
if (workInfo == null) {
return null;
}
String amBegin = workInfo.get("amBegin");
String amEnd = workInfo.get("amEnd");
String pmBegin = workInfo.get("pmBegin");
String pmEnd = workInfo.get("pmEnd");
//step4:开始时间、结束时间和工作日时间对比
int bcr1 = DateUtil.compare(DateUtil.parse(beginTimeStr), DateUtil.parse(amBegin));//1表示前面大,0相等,-1表示前面小
int bcr2 = DateUtil.compare(DateUtil.parse(beginTimeStr), DateUtil.parse(amEnd));
int bcr3 = DateUtil.compare(DateUtil.parse(beginTimeStr), DateUtil.parse(pmBegin));
int bcr4 = DateUtil.compare(DateUtil.parse(beginTimeStr), DateUtil.parse(pmEnd));
int ecr1 = DateUtil.compare(DateUtil.parse(endTimeStr), DateUtil.parse(amBegin));
int ecr2 = DateUtil.compare(DateUtil.parse(endTimeStr), DateUtil.parse(amEnd));
int ecr3 = DateUtil.compare(DateUtil.parse(endTimeStr), DateUtil.parse(pmBegin));
int ecr4 = DateUtil.compare(DateUtil.parse(endTimeStr), DateUtil.parse(pmEnd));
//step4:获取工作时长
if (bcr1 < 0) {//上午上班前
if (ecr1 < 0) {//上午上班前--无需处理
} else if (ecr1 >= 0 && ecr2 < 0) {//上午上班区间
workMills1 = DateUtil.between(DateUtil.parse(dateStr + " " + amBegin)
, DateUtil.parse(dateStr + " " + endTimeStr), DateUnit.MS);
} else if (ecr2 >= 0 && ecr3 < 0) {//中午休息区间
workMills1 = DateUtil.between(DateUtil.parse(dateStr + " " + amBegin)
, DateUtil.parse(dateStr + " " + amEnd), DateUnit.MS);
} else if (ecr3 >= 0 && ecr4 < 0) {//下午上班区间
workMills1 = DateUtil.between(DateUtil.parse(dateStr + " " + amBegin)
, DateUtil.parse(dateStr + " " + amEnd), DateUnit.MS);
workMills2 = DateUtil.between(DateUtil.parse(dateStr + " " + pmBegin)
, DateUtil.parse(dateStr + " " + endTimeStr), DateUnit.MS);
} else if (ecr4 >= 0) {//下午下班时间
workMills1 = DateUtil.between(DateUtil.parse(dateStr + " " + amBegin)
, DateUtil.parse(dateStr + " " + amEnd), DateUnit.MS);
workMills2 = DateUtil.between(DateUtil.parse(dateStr + " " + pmBegin)
, DateUtil.parse(dateStr + " " + pmEnd), DateUnit.MS);
}
} else if (bcr1 >= 0 && bcr2 < 0) {//上午上班区间
if (ecr1 >= 0 && ecr2 < 0) {//上午上班区间
workMills1 = DateUtil.between(DateUtil.parse(dateStr + " " + beginTimeStr)
, DateUtil.parse(dateStr + " " + endTimeStr), DateUnit.MS);
} else if (ecr2 >= 0 && ecr3 < 0) {//中午休息区间
workMills1 = DateUtil.between(DateUtil.parse(dateStr + " " + beginTimeStr)
, DateUtil.parse(dateStr + " " + amEnd), DateUnit.MS);
} else if (ecr3 >= 0 && ecr4 < 0) {//下午上班区间
workMills1 = DateUtil.between(DateUtil.parse(dateStr + " " + beginTimeStr)
, DateUtil.parse(dateStr + " " + amEnd), DateUnit.MS);
workMills2 = DateUtil.between(DateUtil.parse(dateStr + " " + pmBegin)
, DateUtil.parse(dateStr + " " + endTimeStr), DateUnit.MS);
} else if (ecr4 >= 0) {//下午下班时间
workMills1 = DateUtil.between(DateUtil.parse(dateStr + " " + beginTimeStr)
, DateUtil.parse(dateStr + " " + amEnd), DateUnit.MS);
workMills2 = DateUtil.between(DateUtil.parse(dateStr + " " + pmBegin)
, DateUtil.parse(dateStr + " " + pmEnd), DateUnit.MS);
}
} else if (bcr2 >= 0 && bcr3 < 0) {//中午休息区间
if (ecr2 >= 0 && ecr3 < 0) {//中午休息区间--无需处理
} else if (ecr3 >= 0 && ecr4 < 0) {//下午上班区间
workMills2 = DateUtil.between(DateUtil.parse(dateStr + " " + pmBegin)
, DateUtil.parse(dateStr + " " + endTimeStr), DateUnit.MS);
} else if (ecr4 >= 0) {//下午下班时间
workMills2 = DateUtil.between(DateUtil.parse(dateStr + " " + pmBegin)
, DateUtil.parse(dateStr + " " + pmEnd), DateUnit.MS);
}
} else if (bcr3 >= 0 && bcr4 < 0) {//下午上班区间
if (ecr3 >= 0 && ecr4 < 0) {//下午上班区间
workMills2 = DateUtil.between(DateUtil.parse(dateStr + " " + beginTimeStr)
, DateUtil.parse(dateStr + " " + endTimeStr), DateUnit.MS);
} else if (ecr4 >= 0) {//下午下班时间
workMills2 = DateUtil.between(DateUtil.parse(dateStr + " " + beginTimeStr)
, DateUtil.parse(dateStr + " " + pmEnd), DateUnit.MS);
}
} else if (bcr4 >= 0) {//下午下班时间--无需处理
}
System.out.println("计算中-workMills1-" + workMills1);
System.out.println("计算中-workMills2-" + workMills2);
long noWorkMills = allMills - workMills1 - workMills2;
System.out.println("计算中-(allMills - workMills1 - workMills2)-" + noWorkMills);
return noWorkMills;
}
/**
* 判断是否为工作日
*
* @param dateStr yyyy-MM-dd
* @return
*/
public static Boolean isWorkingDay(String dateStr) {
List<String> holiday = initHoliday();
List<String> extraWorkDay = initExtraWorkDay();
//补班日
if (extraWorkDay.contains(dateStr)) {
return true;
}
//节假日
if (holiday.contains(dateStr)) {
return false;
}
//hutool工具jar,5.3.0版本【1是工作日,2-7表示周一到周六】
int week = DateUtil.dayOfWeek(DateUtil.parse(dateStr));
if (week == 1 || week == 7) {
return false;//周末
}
return true;
}
/**
* 初始化节假日【生产环境中请使用配置表】
* 可通过接口获取每年节假日保存到配置表【文档--https://timor.tech/api/holiday】
* String url = "https://timor.tech/api/holiday/year/2023/";
* String rsa = HttpUtil.get(url);
*
* @return
*/
public static List<String> initHoliday() {
List<String> holiday = new ArrayList<>();
holiday.add("2023-01-28");
holiday.add("2023-12-31");
holiday.add("2023-10-06");
holiday.add("2023-10-05");
holiday.add("2023-10-04");
holiday.add("2023-10-03");
holiday.add("2023-10-02");
holiday.add("2023-10-01");
holiday.add("2023-09-30");
holiday.add("2023-09-29");
holiday.add("2023-06-24");
holiday.add("2023-06-23");
holiday.add("2023-06-22");
holiday.add("2023-05-03");
holiday.add("2023-05-02");
holiday.add("2023-05-01");
holiday.add("2023-04-30");
holiday.add("2023-04-29");
holiday.add("2023-04-05");
holiday.add("2023-01-27");
holiday.add("2023-01-26");
holiday.add("2023-01-25");
holiday.add("2023-01-24");
holiday.add("2023-01-23");
holiday.add("2023-01-22");
holiday.add("2023-01-21");
holiday.add("2023-01-02");
holiday.add("2023-01-01");
return holiday;
}
/**
* 初始化额外加班日【生产环境中请使用配置表】
* 可通过接口获取每年节假日保存到配置表【文档--https://timor.tech/api/holiday】
* String url = "https://timor.tech/api/holiday/year/2023/";
*
* @return
*/
public static List<String> initExtraWorkDay() {
List<String> extraWorkDay = new ArrayList<>();
extraWorkDay.add("2023-10-08");
extraWorkDay.add("2023-10-07");
extraWorkDay.add("2023-06-25");
extraWorkDay.add("2023-05-06");
extraWorkDay.add("2023-04-23");
extraWorkDay.add("2023-01-29");
extraWorkDay.add("2023-01-28");
return extraWorkDay;
}
/**
* 初始化工作日工作时间【生产环境中请使用配置表】
*
* @return
*/
public static Map<String, Map<String, String>> initWorkdayInfo() {
Map<String, Map<String, String>> workDayInfoMap = new HashMap<>();
//冬令时
Map<String, String> map = new HashMap<>();
map.put("amBegin", "08:00:00");
map.put("amEnd", "12:00:00");
map.put("pmBegin", "14:00:00");
map.put("pmEnd", "17:30:00");
workDayInfoMap.put("10", map);
workDayInfoMap.put("11", map);
workDayInfoMap.put("12", map);
workDayInfoMap.put("01", map);
workDayInfoMap.put("02", map);
workDayInfoMap.put("03", map);
workDayInfoMap.put("04", map);
//夏令时
map = new HashMap<>();
map.put("amBegin", "08:00:00");
map.put("amEnd", "12:00:00");
map.put("pmBegin", "14:30:00");
map.put("pmEnd", "18:00:00");
workDayInfoMap.put("05", map);
workDayInfoMap.put("06", map);
workDayInfoMap.put("07", map);
workDayInfoMap.put("08", map);
workDayInfoMap.put("09", map);
return workDayInfoMap;
}