【Java开发-工作日计算】使用Java中的LocalDateTime对象计算工作日

功能需求

计算两个日期之间的工作日,要求尽量精准无误。
分析需求如下:本次需求相对比较简单,只需要考虑法定节假日和双休日、调休等即可。

实现设计思路

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);
    }
}


使用运行结果

示例一:
在这里插入图片描述

示例二:
在这里插入图片描述

结尾

以上是此次功能开发过程的一个记录,有用的开发过程,我都会收录到我的开发专题里,欢迎大家互相学习指正,有好的方式或者疑问欢迎在评论区沟通交流,感谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筱星_wu

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值