功能需求
计算两个日期之间的工作日,要求尽量精准无误。
分析需求如下:本次需求相对比较简单,只需要考虑法定节假日和双休日、调休等即可。
实现设计思路
1.以分钟为单位,计算工作日数、工作小时数、工作分钟数
2.规定一天的工作时间,假定开始时间:09:00:00,结束时间:17:00:00
3.在数据库中维护双休日、法定节假日调休等日期信息
4.计算方式采用天数累加方式进行计算,先算出每一天工作时间,在相加(有好的计算方式的小伙伴,欢迎留言讨论)
注:本次日期计算,在计算过程中产生的小数处理,均采用向下保留小数点后位数方式进行小数处理。
引入工具包
无引用包
开发过程问题
无
源码
package com.example.demo;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.Set;
/**
* @author wuxx
* @date 2024/6/25
* @deprecated 计算给定开始时间、结束时间以及节假日列表之间的工作小时数(不包括周末、节假日以及非工作时间,例如每天的晚上和凌晨)
* 早上如果不到9点,按9点开始算;下午如果超过17点,按17点结束算
*/
public class WorkHoursCalculator {
private static final LocalTime WORK_START = LocalTime.of(9, 00); // 上午9点
private static final LocalTime WORK_END = LocalTime.of(17, 00); // 下午5点
private static final String FORMAT_DATE = "yyyy-MM-dd";
private static final String FORMAT_DATETIME_S = "yyyy-MM-dd HH:mm:ss";
private static final String FORMAT_DATETIME_M = "yyyy-MM-dd HH:mm";
private static final int UNIT_HOUR = 60;
/**
*以分钟为单位,计算工作日小时数
* @param startDateStr 开始日期 格式:yyyy-MM-dd HH:mm:ss
* @param endDateStr 结束日期 格式:yyyy-MM-dd HH:mm:ss
* @param holidays 节假日列表,格式:yyyy-MM-dd
* @return double 返回小时数,小数
*/
public static double countWorkHours(String startDateStr, String endDateStr, Set<String> holidays) {
LocalDateTime startDate = strDateToLocalDateTime(startDateStr.substring(0,16), FORMAT_DATETIME_M);
LocalDateTime endDate = strDateToLocalDateTime(endDateStr.substring(0,16), FORMAT_DATETIME_M);
// 转换为日期对象以检查是否为节假日
Set<LocalDate> holidaySet = getHolidaySet(holidays);
double totalWorkHours = 0.0;
LocalDateTime currentDate = startDate;//设置计算的开始时间
//判断结束时间是否超过当天工作结束时间,如果超过则选择当前工作结束时间作为结束时间
if(endDate.with(WORK_END).isBefore(endDate)){
endDate = endDate.with(WORK_END);
}
LocalDateTime currentEndDate = endDate;//设置计算的结束时间
System.out.println("实际开始时间:"+startDate.toLocalTime()+",实际结束时间:"+endDate.toLocalTime());
int i = 0;
//判断当前时间是否超过结束时间
while (!currentDate.isAfter(currentEndDate)) {
i++ ;
double workMinutes = 0;
// 判断当前时间是否在节假日中,如果在节假日中,则跳过该天
if (!holidaySet.contains(currentDate.toLocalDate())) {
// 如果当前时间在工作时间之内,则计算当天的工作小时数,计算公式为:小时数 = 当天结束时间 - 当前时间
LocalDateTime workStartOfDay = currentDate.with(WORK_START);//获取当天工作开始时间
LocalDateTime workEndOfDay = currentDate.with(WORK_END);//获取当天工作结束时间
if (workStartOfDay.isAfter(currentDate)) {
currentDate = workStartOfDay; // 如果当前时间在当天工作开始时间之前,则当前时间设置成当天工作开始时间
}
//判断当天结束时间是否超过结束时间,如果超过,结束时间设置成当天结束时间
if (workEndOfDay.isBefore(currentEndDate) || (workEndOfDay.isEqual(currentEndDate) && currentEndDate.toLocalTime().isBefore(WORK_END))) {
// 如果结束时间在当天结束时间之前或等于结束日期但结束时间在工作时间之内
workMinutes = Duration.between(currentDate, workEndOfDay).toMinutes();
totalWorkHours += workMinutes / UNIT_HOUR;
} else {
// 如果结束时间在当前日期之后,则只计算到当前日期结束的时间
workMinutes = Duration.between(currentDate, currentEndDate).toMinutes();
totalWorkHours += workMinutes / UNIT_HOUR;
}
System.out.println("第"+i+"天:"+workMinutes+"分钟,开始时间:"+currentDate.toLocalTime()+",结束时间:"+workEndOfDay.toLocalTime());
}
currentDate = currentDate.plusDays(1).with(WORK_START); // 增加天数,开始时间变成当天工作开始时间
// 如果当前时间超过了当天的结束时间,则跳到下一天的开始时间
if (currentDate.toLocalTime().isAfter(WORK_END)) {
currentDate = currentDate.plusDays(1).with(WORK_START);
}
}
return totalWorkHours;
}
/**
* 字符串日期转换为LocalDateTime
* @param dateStr 字符串日期
* @param formatter 格式化日期格式
* @return
*/
private static LocalDateTime strDateToLocalDateTime(String dateStr, String formatter) {
return LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(formatter));
}
/**
* 获取周末、节假日集合
* @param holidays 节假日列表(包含周末),数据库维护 格式:yyyy-MM-dd
* @return
*/
private static Set<LocalDate> getHolidaySet(Set<String> holidays){
Set<LocalDate> holidaySet = new HashSet<>();
for (String holidayStr : holidays) {
holidaySet.add(LocalDate.parse(holidayStr, DateTimeFormatter.ofPattern(FORMAT_DATE)));
}
return holidaySet;
}
/**
* 计算工作天数,向下取,保留一位小数
* @param startDateStr 开始日期 格式:yyyy-MM-dd HH:mm:ss
* @param endDateStr 结束日期 格式:yyyy-MM-dd HH:mm:ss
* @param holidays 节假日列表,格式:yyyy-MM-dd
* @return 返回工作天数,小数
*/
public static double getCountWorkDays(String startDateStr, String endDateStr, Set<String> holidays){
double workHours = countWorkHours(startDateStr, endDateStr, holidays);
return getRounding(workHours/8, 1);
}
/**
* 格式化小数,向下取
* @param value 原始值
* @param scale 保留小数位数
* @return
*/
private static double getRounding(double value,int scale){
BigDecimal originalValue = new BigDecimal(value);
return originalValue.setScale(scale, RoundingMode.DOWN).doubleValue();
}
/**
* 示例
* @param args
*/
public static void main(String[] args) {
// 示例输入
String startDateStr = "2024-05-31 10:32:30"; // 开始计算的小时(早于工作时间也可以)
String endDateStr = "2024-06-06 21:55:05"; // 结束计算的小时(晚于工作时间也可以)
Set<String> holidays = new HashSet<>();
holidays.add("2024-06-22"); // 示例节假日
holidays.add("2024-06-23"); // 示例节假日
holidays.add("2024-06-16");
holidays.add("2024-06-01");
holidays.add("2024-06-02");
// 计算工作小时数
double workHours = getCountWorkDays(startDateStr, endDateStr, holidays);
// 输出结果
System.out.println("工作天数: " + workHours);
}
}
使用运行结果
示例一:
示例二:
结尾
以上是此次功能开发过程的一个记录,有用的开发过程,我都会收录到我的开发专题里,欢迎大家互相学习指正,有好的方式或者疑问欢迎在评论区沟通交流,感谢!