请假时间计算(支持任何时间段)

本文介绍了如何根据业务需求计算请假时长,考虑了节假日、双休、调休、午休等因素。通过两个工具类的配合,实现了2019全年请假时间计算,对于其他年份,需要额外录入数据。

因为业务需求,要对请假时间进行计算,也就是用户选定一个开始时间,在选定一个结束时间,你要给用户展示一个他请假的时长,这个时长肯定是除开节假日,双休,其中考虑到放假后的调休,中午午休,以及用户提交请假所处的时间段。
翻了翻网上的一些带佬的代码(毕竟码农的精华就在CV嘛),恩,有重合部分,但无法完全满足业务需求,所以还是需要自己动手做部分整合,最关键是部分代码需要调用外部接口,也把整合的代码放出。

第一个工具类:

package com.api.leave.utils;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
  * 请假计算(小时)
  * 1.除去 节假日
  * 2.除去 其他规定的 非工作时间(有具体的  实行者指定)
  * 3.考虑一点 其他不上班时间 有些公司的特别
  * @author fengjing
  *
  */
public class HolidayDateUtil {

/**
 * 节假日查询接口
 * 使用方法 url+yyyyMMdd
 */


//时间格式化
public final static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public final static SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd");

/**
 * 从公认网站上获取 节假日信息
 * @param url 获取节假日的 公认网站 url="网站地址"+时间 时间格式yyyyMMdd
 * @param url
 * @return 工作日对应结果为 0, 休息日对应结果为 1, 节假日对应的结果为 2, 网站失效-1, 数据格式改变-2
 */
public static String getHoliday(String date) {
    ChineseCalendarUtils cc = new ChineseCalendarUtils();
    try {
        return cc.checkDate(date);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

/**
 * 判断某年某月有多少天
 * @param year 年  "yyyy"
 * @param moun 月 "m"
 * @return
 */
public static int daysMonth(int year,int moun){
    switch(moun){
        case 0: return 31;
        case 1: return ((year%4==0&&year%100!=0)||year%400==0)?29:28;
        case 2: return 31;
        case 3: return 30;
        case 4: return 31;
        case 5: return 30;
        case 6: return 31;
        case 7: return 31;
        case 8: return 30;
        case 9: return 31;
        case 10: return 30;
        case 11: return 31;
        default: return 0;
    }
}
/**
 * 通过时间秒毫秒数判断两个时间的间隔
 * @param tiny 开始时间
 * @param large结束时间
 * @return 返回几天
 */
public static int differentDaysByMillisecond(Date tiny,Date large){
    if(format1.format(tiny).equals(format1.format(large))){
        return 0;
    }
    Calendar c1 = Calendar.getInstance();
    Calendar c2 = Calendar.getInstance();
    c1.setTime(tiny);
    c2.setTime(large);
    int result = c2.get(Calendar.DAY_OF_YEAR) - c1.get(Calendar.DAY_OF_YEAR);
    /*Double why = Double.valueOf((large.getTime() - tiny.getTime()))/(1000*3600*24);
    double d = Math.ceil(why);*/
    return result;
}

/**
 * 通过时间秒毫秒数判断两个时间的间隔
 * @param tiny 开始时间
 * @param large结束时间
 * @return 返回几小时
 */
public static double differentHoursByMillisecond(Date tiny,Date large){
    double hours = ((double) ((large.getTime() - tiny.getTime())) / (1000*3600));
    return hours;
}

/**
 * 时间格式yyyy-MM-dd HH:mm:ss
 * @param start 请假开始时间 yyyy-MM-dd HH:mm:ss
 * @param end 请假结束时间 yyyy-MM-dd HH:mm:ss
 * @param startM 上班早晨开始时间 HH:mm:ss
 * @param endE   上班早晨结束时间 HH:mm:ss
 * @param startAf 上班下午开始时间 HH:mm:ss
 * @param endAf      上班下午结束时间 HH:mm:ss
 * @return
 */
public double calculateTimeHour(String start,String end,String startM,String endE,String startAf,String endAf){
    if(start!=null&&end!=null&&startM!=null&&endE!=null&&startAf!=null&&endAf!=null){//确保时间格式正确和值存在
        try {
            Date dataStart = HolidayDateUtil.format.parse(start);
            Date dataEnd = HolidayDateUtil.format.parse(end);

            double hours = 0;
            int monthDays = -1;
            int yearTime = dataStart.getYear()+1900;
            int mounthTime = dataStart.getMonth();
            int dataTime = dataStart.getDate();

            double lunchBreak;
            int interval = HolidayDateUtil.differentDaysByMillisecond(dataStart,dataEnd);

            //判断这年这月有多少天
            monthDays = HolidayDateUtil.daysMonth(yearTime,mounthTime);
            //第一天请假开始时间
            String startday = start.substring(start.lastIndexOf(" ")+1,start.length()-1);
            //最后一天请假结束时间
            String endday = end.substring(end.lastIndexOf(" ")+1,end.length()-1);
            double totalDay = 0;//累积总共请假多少天
            for(int i=0;i<=interval;i++){
                if(interval==0){//间隔0天 也就是 请假在一天之内
                    if(!"0".equals(HolidayDateUtil.getHoliday(yearTime+"-"+(((mounthTime+1))>=10?(mounthTime+1):"0"+(mounthTime+1))+"-"+(dataTime>10?dataTime:"0"+dataTime)))){
                        System.out.println(""+yearTime+(((mounthTime+1))>10?(mounthTime+1):"0"+(mounthTime+1))+(dataTime>10?dataTime:"0"+dataTime)+"非工作日期不需要计算");
                        return 0;
                    }
                    String s = start.substring(0,10)+" "+endE;
                    String s1 = start.substring(0,10)+" "+startAf;
                    String s2 = start.substring(0,10)+" "+startM;
                    String s3 = start.substring(0,10)+" "+endAf;
                    Date dataMorning = HolidayDateUtil.format.parse(s2);
                    Date dataAfternoon = HolidayDateUtil.format.parse(s3);
                    Date dataCenter1 = HolidayDateUtil.format.parse(s);
                    Date dataCenter2 = HolidayDateUtil.format.parse(s1);
                    //开始时间在9点前 结束时间:①9点前 ②9点到12点 ③12点到1点 ④1点到6点 ⑤6点后
                    if(dataStart.compareTo(dataMorning) <= 0){
                        if(dataEnd.compareTo(dataMorning) <= 0){
                            hours = 0;
                            lunchBreak = 0;
                        }else if(dataEnd.compareTo(dataMorning) > 0 && dataEnd.compareTo(dataCenter1) <= 0){
                            hours = differentHoursByMillisecond(dataMorning,dataEnd);
                            lunchBreak = 0;
                        }else if(dataEnd.compareTo(dataCenter1) > 0 && dataEnd.compareTo(dataCenter2) <= 0){
                            hours = differentHoursByMillisecond(dataMorning,dataCenter1);
                            lunchBreak = 0;
                        }else if(dataEnd.compareTo(dataCenter2) > 0 && dataEnd.compareTo(dataAfternoon) <= 0){
                            hours = differentHoursByMillisecond(dataMorning,dataEnd);
                            lunchBreak = 1;
                        }else{
                            hours = 8;
                            lunchBreak = 0;
                        }
                    }
                    //开始时间在9点到12点 结束时间:②9点到12点 ③12点到1点 ④1点到6点 ⑤6点后
                    else if(dataStart.compareTo(dataMorning) > 0 && dataStart.compareTo(dataCenter1) <= 0){
                        if(dataEnd.compareTo(dataMorning) > 0 && dataEnd.compareTo(dataCenter1) <= 0){
                            hours = differentHoursByMillisecond(dataStart,dataEnd);
                            lunchBreak = 0;
                        }else if(dataEnd.compareTo(dataCenter1) > 0 && dataEnd.compareTo(dataCenter2) <= 0){
                            hours = differentHoursByMillisecond(dataStart,dataEnd);
                            lunchBreak = differentHoursByMillisecond(dataCenter1,dataEnd);
                        }else if(dataEnd.compareTo(dataCenter2) > 0 && dataEnd.compareTo(dataAfternoon) <= 0){
                            hours = differentHoursByMillisecond(dataStart,dataEnd);
                            lunchBreak = 1;
                        }else{
                            hours = differentHoursByMillisecond(dataStart,dataAfternoon);
                            lunchBreak = 1;
                        }
                    }
                    //开始时间在12点到1点 结束时间:③12点到1点 ④1点到6点 ⑤6点后
                    else if(dataStart.compareTo(dataCenter1) > 0 && (dataStart.compareTo(dataCenter2)) <= 0){
                        if(dataEnd.compareTo(dataCenter1) > 0 && dataEnd.compareTo(dataCenter2) <= 0){
                            hours = 0;
                            lunchBreak = 0;
                        }else if(dataEnd.compareTo(dataCenter2) > 0 && dataEnd.compareTo(dataAfternoon) <= 0){
                            hours = differentHoursByMillisecond(dataCenter2,dataEnd);
                            lunchBreak = 0;
                        }else{
                            hours = differentHoursByMillisecond(dataStart,dataAfternoon);
                            lunchBreak = differentHoursByMillisecond(dataStart,dataCenter2);
                        }
                    }
                    //开始时间在1点到6点 结束时间: ④1点到6点 ⑤6点后
                    else if(dataStart.compareTo(dataCenter2) >0 && dataStart.compareTo(dataAfternoon) <= 0){
                        if(dataEnd.compareTo(dataCenter2) > 0 && dataEnd.compareTo(dataAfternoon) <= 0){
                            hours = differentHoursByMillisecond(dataStart,dataEnd);
                            lunchBreak = 0;
                        }else{
                            hours = differentHoursByMillisecond(dataStart,dataAfternoon);
                            lunchBreak = 0;
                        }
                    }
                    //开始时间在6点后
                    else{
                        hours = 0;
                        lunchBreak = 0;
                    }


                }else{//间隔超过一天
                    dataTime +=(i==0?0:1);
                    if(dataTime>monthDays){
                        mounthTime ++;
                        dataTime=1;
                        if(mounthTime>11){
                            yearTime++;
                            mounthTime=0;
                            dataTime=1;
                        }
                        monthDays = HolidayDateUtil.daysMonth(yearTime,mounthTime);
                    }

                    //判断是否是节假日
                    if(!"0".equals(HolidayDateUtil.getHoliday(yearTime+"-"+(((mounthTime+1))>=10?(mounthTime+1):"0"+(mounthTime+1))+"-"+(dataTime>10?dataTime:"0"+dataTime)))){
                        System.out.println(""+yearTime+(((mounthTime+1))>10?(mounthTime+1):"0"+(mounthTime+1))+(dataTime>10?dataTime:"0"+dataTime)+"非工作日期不需要计算");
                        continue;
                    }
                    String s1 = yearTime+"-"+(((mounthTime+1))>=10?(mounthTime+1):"0"+(mounthTime+1))+"-"+(dataTime>=10?dataTime:"0"+dataTime)+" "+(i==0?startday:startM);
                    String s2 = yearTime+"-"+(((mounthTime+1))>=10?(mounthTime+1):"0"+(mounthTime+1))+"-"+(dataTime>=10?dataTime:"0"+dataTime)+" "+(i==interval?endday:endAf);
                    Date dateMorning = HolidayDateUtil.format.parse(s1.substring(0,10)+" "+startM);
                    Date dateAfternoon = HolidayDateUtil.format.parse(s1.substring(0,10)+" "+endAf);
                    Date dateCenter1 = HolidayDateUtil.format.parse(s1.substring(0,10)+" "+endE);
                    Date dateCenter2 = HolidayDateUtil.format.parse(s1.substring(0,10)+" "+startAf);
                    dataStart = HolidayDateUtil.format.parse(s1);
                    dataEnd = HolidayDateUtil.format.parse(s2);
                    //第一天 dataEnd为6点 判断开始时间 ①9点前 ②9点到12点 ③12点到1点 ④ 1点到6点 ⑤ 6点之后
                    if(i == 0){
                        if(dataStart.compareTo(dateMorning) <= 0){
                            hours = 8;
                            lunchBreak = 0;
                        }else if(dataStart .compareTo(dateMorning) > 0 && dataStart.compareTo(dateCenter1) <= 0){
                            hours = differentHoursByMillisecond(dataStart,dataEnd);
                            lunchBreak = 1;
                        }else if(dataStart.compareTo(dateCenter1) > 0 && dataStart.compareTo(dateCenter2) <= 0){
                            hours = differentHoursByMillisecond(dataStart,dataEnd);
                            lunchBreak = differentHoursByMillisecond(dateCenter1,dataStart);
                        }else if(dataStart.compareTo(dateCenter2) > 0 && dataStart.compareTo(dateAfternoon) <= 0){
                            hours = differentHoursByMillisecond(dataStart,dataEnd);
                            lunchBreak = 0;
                        }else{
                            hours = 0;
                            lunchBreak = 0;
                        }
                        //中间天 hours 为8 lunchBreak为0
                    }else if( i != 0 && i != interval){
                        hours = 8;
                        lunchBreak = 0;
                        //最后一天 dataStart为9点 判断结束时间 ①9点前 ②9点到12点 ③12点到1点 ④ 1点到6点 ⑤ 6点之后
                    }else{
                        if(dataEnd.compareTo(dateMorning) <= 0){
                            hours = 0;
                            lunchBreak = 0;
                        }else if(dataEnd .compareTo(dateMorning) > 0 && dataEnd.compareTo(dateCenter1) <= 0){
                            hours = differentHoursByMillisecond(dataStart,dataEnd);
                            lunchBreak = 0;
                        }else if(dataEnd.compareTo(dateCenter1) > 0 && dataEnd.compareTo(dateCenter2) <= 0){
                            hours = differentHoursByMillisecond(dataStart,dataEnd);
                            lunchBreak = differentHoursByMillisecond(dateCenter1,dataEnd);
                        }else if(dataEnd.compareTo(dateCenter2) > 0 && dataEnd.compareTo(dateAfternoon) <= 0){
                            hours = differentHoursByMillisecond(dataStart,dataEnd);
                            lunchBreak = 1;
                        }else{
                            hours = 8;
                            lunchBreak = 0;
                        }
                    }
                }
                System.out.println(yearTime+"-"+(((mounthTime+1))>10?(mounthTime+1):"0"+(mounthTime+1))+"-"+(dataTime>10?dataTime:"0"+dataTime)+" 请假:"+(hours-lunchBreak)+"小时");
                totalDay+=hours-lunchBreak;
            }
            System.out.println(totalDay);
            return totalDay;
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
    return -1;
}

/**
 * 累积请假共多少天
 * @param totalDay
 * @return totalDay < 24小时按小时算   totalDay >= 24小时按天算
 */
public String totalDay(double totalDay){
    if(totalDay > 24){
        return (totalDay/24) + "天";
    }else{
        return totalDay + "小时";
    }
}

public static void main(String args[]) throws IOException{
    HolidayDateUtil qj= new HolidayDateUtil();
    //测试
    qj.calculateTimeHour("2019-9-01 09:00:00","2019-10-09 12:00:00","09:00:00","12:00:00","13:00:00","18:00:00");
}
}

第二个工具类:

package com.api.leave.utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;


/**
 * @author  qyw
 * @description  日期日历工具类
 * @date Created in 21:01 2019/1/31
 **/
public class ChineseCalendarUtils {

    // 法律规定的放假日期
    private List<String> lawHolidays   = Arrays.asList("2019-9-13", "2019-9-14",
            "2019-09-15", "2019-10-01", "2019-10-02", "2019-10-03", "2019-10-04",
            "2019-10-05", "2019-10-06", "2019-10-07");
    // 由于放假需要额外工作的周末
    private List<String> extraWorkdays = Arrays.asList(
            "2019-09-16", "2019-09-29", "2019-10-12", "2019-10-08");

    /**
     * @author  qyw
     * @description  判断是否是法定假日
     * @date Created in 21:03 2019/1/31
     **/
    public boolean isLawHoliday(String calendar) throws Exception {
        ChineseCalendarUtils.isValidDate(calendar);
        if (lawHolidays.contains(calendar)) {
            return true;
        }
        return false;
    }



    /**
     * @author  qyw
     * @description  判断是否是周末
     * @date Created in 21:03 2019/1/31
     **/
    public boolean isWeekends(String calendar) throws Exception {
        ChineseCalendarUtils.isValidDate(calendar);
        // 先将字符串类型的日期转换为Calendar类型
        SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd");
        Date             date = sdf.parse(calendar);
        Calendar         ca   = Calendar.getInstance();
        ca.setTime(date);
        if (ca.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY
                || ca.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
            return true;
        }
        return false;
    }

    /**
     * @author  qyw
     * @description  判断是否是需要额外补班的周末
     * @date Created in 21:06 2019/1/31
     **/
    public boolean isExtraWorkday(String calendar) throws Exception {
        ChineseCalendarUtils.isValidDate(calendar);
        if (extraWorkdays.contains(calendar)) {
            return true;
        }
        return false;
    }

    /**
     * @author  qyw
     * @description  判断是否是休息日(包含法定节假日和不需要补班的周末)
     * @date Created in 21:06 2019/1/31
     **/
    public boolean isHoliday(String calendar) throws Exception {
        ChineseCalendarUtils.isValidDate(calendar);
        // 首先法定节假日必定是休息日
        if (this.isLawHoliday(calendar)) {
            return true;
        }
        // 排除法定节假日外的非周末必定是工作日
        if (!this.isWeekends(calendar)) {
            return false;
        }
        // 所有周末中只有非补班的才是休息日
        if (this.isExtraWorkday(calendar)) {
            return false;
        }
        return true;
    }

    /**
     * @author  qyw
     * @description  校验字符串是否为指定的日期格式,含逻辑严格校验,2007/02/30返回false
     * @date Created in 21:06 2019/1/31
     **/
    private static boolean isValidDate(String str) {
        boolean convertSuccess = true;
        // 指定日期格式为四位年/两位月份/两位日期,注意yyyy-MM-dd区分大小写;
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        try {
            // 设置lenient为false.
            // 否则SimpleDateFormat会比较宽松地验证日期,比如2007/02/29会被接受,并转换成2007/03/01
            format.setLenient(false);
            format.parse(str);
        } catch (ParseException e) {
            convertSuccess = false;
        }
        return convertSuccess;
    }


    public String checkDate(String date) throws Exception {

    if(isHoliday(date)){
        return "-1";
    }else{
        return "0";
    }
}
}

两个工具类配合使用就行,篇幅较长,建议CV到idea上阅读,支持2019全年度请假时间计算,如需支持其他年份,需要再行录入部分数据。

2026年美国大学生数学建模竞赛(MCM/ICM),C题高质量成品文章、完整解题代码及所有结果表。内容涵盖Python,MATLAB版本代码、Word论文文档无水印(Word版本),并包含详细思路解析,助力参赛队伍高效完成比赛,冲刺高奖项。【核心内容】:2026年成品论文包含完整解题思路、模型构建、数据分析与结果讨论。格式规范,符合竞赛论文要求,可直接提交或稍作修改使用。完整代码提供Python和MATLAB语言实现,覆盖数据处理、模型训练、结果可视化全流程。代码模块化设计,注释清晰,便于理解与二次开发。结果表格所有实验数据与结果均已整理成表,直观展示模型性能与对比分析。提供一键转换工具,方便用户根据需求调整论文格式。【核心交付清单】:2026年成品论文(Word)可直接提交Python、MATLAB代码包数据集与运行结果图和结果表持续更新,多套方案,杜绝雷同【赠送资料200GB】:2006-2025历年赛题(中英双语版+完整附件)2005-2025O奖/F奖获奖论文(含中文翻译+规范格式)2024-2025赛题深度解析(附解题思路+可运行代码)2026专属LaTeX+Word论文模板(直接套用,格式零出错)55种可视化方法+Python即插即用代码(填数据就能跑)【产品优势】:高效实用:成品论文与代码已通过严格测试,确保结果准确且可复现。全面覆盖:从思路解析到最终成果,一站式解决参赛需求。灵活便捷:支持多平台使用,网盘直发,后续更新免费获取。- 分类清晰:从赛题到论文,从模板到代码,按年份和题型整理,找资料不迷路- 中英对照:获奖论文和赛题全翻译,避免理解偏差,效率直接拉满- 实战导向:O奖论文的逻辑框架、代码模块都能直接参考,帮你快速搭建自己的解题模型 ABCDEF题全套电子资料,秒发网盘链接!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值