公司业务需求,在请假时自动跳过周六周日以及节假日。通过statisticsOfLeaveTime方法,传入请假开始的时间以及请假结束的时间,则可自动统计调休时间。
代码整体逻辑就为拿到请假开始和结束时间,分三种情况进行计算请假时长,第一种情况为请假一天,开始和结束时间均为一天,第二种情况为请假连续两天,第三种情况为请假超过2天。
请假一天时,分为包含午休和不包含午休两种情况,包含午休时需要减去午休时间,不包含午休时直接求时间的数据差即可。
请假两天时,需要分别单独计算第一天和第二天的时间和,第一天请假结束时间就设置为下班时间,第二天请假开始时间就设置为上班时间。
请假三天及以上时,单独求出请假第一天和最后一天的时间,再循环期间的日期判断是否在周末或节假日内,如果在周末或节假日内则跳过时间计算,如果不是周末或节假日则加上一天的请假时间。
以下代码中重点部分都存在注释,其中的周末周日规则是前往数据库查询公司规定的工作日时间,若不在这期间内则作为周末时间,其中节假日时间也是前往数据库查询的公司规定的节假日时间范围,如果在这个范围内则做为节假日时间。
公司一天上班7.5小时,但是请假一天要按8小时计算,所以在一天请假时长计算时添加了如果分钟数达到一天7.5小时的话设置为8小时的分钟数,公司人性化请假少于5分钟的时长不计算,所以返回时做了时间计算,多于5分钟部分算为一小时,少于5分钟部分不作计算。
最后,此方法传入的开始时间和结束时间都是使用SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");进行格式化的时间,如果没有格式话会报异常。
//上班时间
static LocalTime workDayStartTime = LocalTime.of(9, 0);
//下班时间
static LocalTime workDayEndTime = LocalTime.of(18, 0);
//午休开始时间
static LocalTime workDayNoonStartTime = LocalTime.of(12, 0);
//午休结束时间
static LocalTime workDayNoonEndTime = LocalTime.of(13, 30);
/**
* 获取请假时长,排除周六周日及节假日
* @param beginTime
* @param endTime
* @return
* @throws ParseException
*/
@Override
public Integer statisticsOfLeaveTime(String beginTime, String endTime) throws ParseException {
LocalDateTime startDateTime = LocalDateTime.parse(beginTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
LocalDateTime endDateTime = LocalDateTime.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
LocalDate startDate = startDateTime.toLocalDate();
LocalDate endDate = endDateTime.toLocalDate();
//结束请假当天的工作开始时间
LocalDateTime endWorkStartDateTime = LocalDateTime.of(endDate, workDayStartTime);
//结束请假当天的工作结束时间
LocalDateTime workEndDateTime = LocalDateTime.of(endDate, workDayEndTime);
//开始请假当天的工作开始时间
LocalDateTime workDayStartDateTime = LocalDateTime.of(startDate, workDayStartTime);
//开始工作当天的工作结束时间
LocalDateTime workDayEndDateTime = LocalDateTime.of(endDate, workDayEndTime);
//两个if判断请假开始时间和结束时间是否在工作时间内,在工作时间外则赋值为工作时间
if (startDateTime.isBefore(workDayStartDateTime)) {
startDateTime = workDayStartDateTime;
}
if (endDateTime.isAfter(workDayEndDateTime)) {
endDateTime = workDayEndDateTime;
}
//判断结束请假时间,如果在结束当天的上班时间之前,赋值为上班时间
if (endDateTime.isBefore(endWorkStartDateTime)) {
endDateTime = endWorkStartDateTime;
}
//返回时间总数(分钟)
long leaveHours = 0;
//获取请假天数间隔,0为一条,1为连续两天,否则为连续多头请假
long sumDate = endDate.toEpochDay() - startDate.toEpochDay();
if (sumDate == 0) {
leaveHours = sumOneDay(startDateTime, endDateTime);
} else if (sumDate == 1) {
//分别获取两天的时间
leaveHours += sumOneDay(startDateTime, workEndDateTime);
leaveHours += sumOneDay(endWorkStartDateTime, endDateTime);
} else {
//获取请假开始那天和请假结束那天时间
leaveHours += sumOneDay(startDateTime, workEndDateTime);
leaveHours += sumOneDay(endWorkStartDateTime, endDateTime);
//循环请假中间时间,不在假期内则增加一条请假时间
for (LocalDate date = startDate.plusDays(1); !date.isAfter(endDate.plusDays(-1)); date = date.plusDays(1)) {
if (isHoliday(date)) {
continue;
}
leaveHours += 480;
}
}
//返回请假时长,将分钟转化为小时,超过5分钟则计为一小时
return toHours(leaveHours);
}
/**
* 计算请假一天的请假时长
*/
public int sumOneDay(LocalDateTime startDateTime, LocalDateTime endDateTime) {
int leaveHours = 0;
LocalDate startDate = startDateTime.toLocalDate();
LocalDate endDate = endDateTime.toLocalDate();
//获取当天午休时间
LocalDateTime workDayNoonStartDateTime = LocalDateTime.of(startDate, workDayNoonStartTime);
LocalDateTime workDayNoonEndDateTime = LocalDateTime.of(endDate, workDayNoonEndTime);
//请假时间段包含午休进if
if (startDateTime.isBefore(workDayNoonEndDateTime) && endDateTime.isAfter(workDayNoonStartDateTime)) {
//判断请假开始时间是否在午休开始时间前,在午休开始时间之后则不用计算上午请假时间
LocalDateTime leaveStartDateTime = startDateTime.isBefore(workDayNoonStartDateTime) ? startDateTime : null;
//判断请假结束时间是否在午休结束时间前,在午休结束时间之前则不用计算下午请假时间
LocalDateTime leaveEndDateTime = endDateTime.isAfter(workDayNoonEndDateTime) ? endDateTime : null;
if (leaveStartDateTime != null) {
leaveHours += Duration.between(leaveStartDateTime, workDayNoonStartDateTime).toMinutes();
}
if (leaveEndDateTime != null) {
leaveHours += Duration.between(workDayNoonEndDateTime, leaveEndDateTime).toMinutes();
}
} else {
leaveHours += Duration.between(startDateTime, endDateTime).toMinutes();
}
//如果刚刚好一天都请假了则算为8小时
if (leaveHours == 450) {
leaveHours = 480;
}
return leaveHours;
}
/**
* 是否为放假时间判断
*/
public boolean isHoliday(LocalDate date) throws ParseException {
Boolean is = false;
//获取当前年
Integer curyear = DateUtils.getYear(new Date());
//获取当前年考勤规则
OaSignRule rule = ruleService.queryFirstOne(curyear);
//获取考勤规则中的工作时间
String workingDays = rule.getWorkingDays();
Date date1 = Date.from(date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
Calendar cal = Calendar.getInstance();
cal.setTime(date1);
//判断当前天是否在考勤规则中的工作时间
if (!StringUtils.isEmpty(workingDays)) {
List list = Arrays.asList(workingDays.split(","));
String week = String.valueOf(cal.get(Calendar.DAY_OF_WEEK) - 1);
if (!list.contains(week)) {
return true;
}
}
//判断当前天是否在设置的节假日里
Long currentDateTime = date1.getTime();
is = checkToDay(currentDateTime, rule.getHolidayYuanDan(), is);
is = checkToDay(currentDateTime, rule.getHolidayChunJie(), is);
is = checkToDay(currentDateTime, rule.getHolidayQingMing(), is);
is = checkToDay(currentDateTime, rule.getHolidayLaoDong(), is);
is = checkToDay(currentDateTime, rule.getHolidayDuanWu(), is);
is = checkToDay(currentDateTime, rule.getHolidayZhongQiu(), is);
is = checkToDay(currentDateTime, rule.getHolidayGuoQing(), is);
is = checkToDay(currentDateTime, rule.getHolidayOther(), is);
return is;
}
/**
* 判断是否为假期
*/
public boolean checkToDay(Long currentDate, String holiday, Boolean is) throws ParseException {
if (is) {
return true;
}
if (!StringUtils.isEmpty(holiday)) {
String[] arr = holiday.split(",");
long beginTime = DateUtils.parseDate(arr[0], "yyyy-MM-dd").getTime();
long endTime = DateUtils.parseDate(arr[1], "yyyy-MM-dd").getTime();
if (currentDate >= beginTime && currentDate <= endTime) {
return true;
}
}
return false;
}
/**
* 将分钟转化为小时,超过五分钟算为一个小时
*/
public Integer toHours(long minutes) {
int moreMinutes = (int) minutes % 60;
if (moreMinutes <= 5) {
return (int) Math.floor((double) minutes / 60);
} else {
return (int) Math.ceil((double) minutes / 60);
}
}