计算上班工作日的工时数
最近公司要求去计算工时数:
公司上班情况:
1.早上8:30 到 下午的 17:30 , 中午午休时间 12:00 - 13:00 午休一个小时
2.只计算周一到周五,上班的时间,周六日工时数去除
3.午休的一个小时不包含在工作数中
4.设计到跨天计算,比如计算2024-08-27 9:00 到 2024-8-29 17:00
针对以上这种情况,提供如下方法来解决
// 定义工作开始和结束时间,午休开始和结束时间
private static final LocalTime WORK_START = LocalTime.of(8, 30);
private static final LocalTime WORK_END = LocalTime.of(17, 30);
private static final LocalTime BREAK_START = LocalTime.of(12, 0);
private static final LocalTime BREAK_END = LocalTime.of(13, 0);
@Override
public double calculateWorkingHoursAcrossDays(ConfirmHours confirmHours) {
// 开始时间和结束时间
LocalDateTime startTime = confirmHours.getBeginTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDateTime endTime = confirmHours.getEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
boolean addLunchWorkHour = confirmHours.isAddLunchWorkHour();
// 跨天情况
if (startTime.toLocalDate().isBefore(endTime.toLocalDate())) {
return roundToTwoDecimalPlaces(calculateCrossDayWorkMinutes(startTime, endTime,addLunchWorkHour) / 60.0); // 将分钟转换为小时并显示
} else {
// 同一天的情况
return roundToTwoDecimalPlaces(calculateSingleDayWorkMinutes(startTime, endTime, addLunchWorkHour) / 60.0);
}
}
// 将数字保留两位小数
private double roundToTwoDecimalPlaces(double number) {
DecimalFormat df = new DecimalFormat("#.##");
return Double.parseDouble(df.format(number));
}
private static long calculateCrossDayWorkMinutes(LocalDateTime startTime, LocalDateTime endTime,boolean addLunchWorkHour) {
LocalDateTime endOfFirstDay = LocalDateTime.of(startTime.toLocalDate(), WORK_END);
LocalDateTime startOfSecondDay = LocalDateTime.of(endTime.toLocalDate(), WORK_START);
long firstDayWorkMinutes = calculateSingleDayWorkMinutes(startTime, endOfFirstDay,addLunchWorkHour);
long secondDayWorkMinutes = calculateSingleDayWorkMinutes(startOfSecondDay, endTime,addLunchWorkHour);
// 计算中间完整工作天数,排除周末
long fullDays = ChronoUnit.DAYS.between(startTime.toLocalDate().plusDays(1), endTime.toLocalDate());
long workDayCount = 0;
for (int i = 0; i < fullDays; i++) {
LocalDate tempDate = startTime.toLocalDate().plusDays(i + 1);
if (isWorkDay(tempDate.atStartOfDay())) {
workDayCount++;
}
}
// 计算中间完整工作日的工时(按分钟计算)
long fullDayWorkMinutes = 480;
if (addLunchWorkHour) {
fullDayWorkMinutes += 60;
}
return firstDayWorkMinutes + secondDayWorkMinutes + (workDayCount * fullDayWorkMinutes);
}
private static long calculateSingleDayWorkMinutes(LocalDateTime startTime, LocalDateTime endTime, boolean addLunchWorkHour) {
// 如果是周末,返回0工时
if (!isWorkDay(startTime) || !isWorkDay(endTime)) {
return 0;
}
// 限制时间范围在工作时间内
LocalTime start = startTime.toLocalTime().isBefore(WORK_START) ? WORK_START : startTime.toLocalTime();
LocalTime end = endTime.toLocalTime().isAfter(WORK_END) ? WORK_END : endTime.toLocalTime();
// 如果调整后的start时间比end时间大(跨天情况),则结束时间设为WORK_END
if (start.isAfter(end)) {
return 0;
}
Duration workDuration = Duration.between(start, end);
// 如果addLunchWorkHour为false,并且工作时间和午休时间重叠,减去午休时间
if (!addLunchWorkHour && start.isBefore(BREAK_END) && end.isAfter(BREAK_START)) {
LocalTime breakStart = BREAK_START.isBefore(start) ? start : BREAK_START;
LocalTime breakEnd = BREAK_END.isAfter(end) ? end : BREAK_END;
// 只有在工作时间与午休时间重叠的情况下才减去午休时间
if (breakStart.isBefore(breakEnd)) {
Duration breakDuration = Duration.between(breakStart, breakEnd);
workDuration = workDuration.minus(breakDuration);
}
}
return workDuration.toMinutes();
}
// 判断是否是工作日(周一到周五)
private static boolean isWorkDay(LocalDateTime dateTime) {
DayOfWeek dayOfWeek = dateTime.getDayOfWeek();
return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY;
}
工时按分钟计算:
修改后的方法 calculateWorkMinutes 计算总工时的分钟数,避免了之前只按小时计算而导致的精度问题。
跨天工作时间的计算:
第一天从 12:30 到 17:30:
总共 5 小时(300 分钟),减去午休时间 30 分钟,所以第一天的工时是 270 分钟。
第二天从 08:30 到 13:30:
08:30 到 12:00 是 3.5 小时(210 分钟),13:00 到 13:30 是 0.5 小时(30 分钟),合计为 240 分钟。
总计为 270 + 240 = 510 分钟,即 8.5 小时。