java计算日期不包括周末、不包括节假日、不包括非工作时间、考虑加班时间

需求:

1、给出开始时间beginDate,结束时间endDate,计算两个时间之间的时间差

2、给出一个时间nowDate,计算其加上x小时之后的时间。

注:

时间的计算不能计算非工作时间。即,不包括周末、不包括节假日、不包括非工作小时、考虑正常加班时间

例如:工作时间为:【8:00~12:00,14:00~18:00】

例如:国庆假期上班第一天10月9号是周日,这种情况时间也要计算在内

思路:

取开始时间,判断是否是节假日、是否是周末,是否是加班日

如果是节假日,开始日期往后延一天,然后再递归判断

如果是周末,判断是否是加班日,如果不是加班日,开始日期往后延一天,然后再递归判断

(看了有些网友写的方案计算两个日期的全部时间,然后再减去不符合条件的时间。我反其道而思考,只考虑符合条件的日期,求和,逻辑略复杂,但是直观易懂)

源码:

public class DateCal
{
    //法定节假日
    private final String isHoliday = "1-1,1-2,1-3,2-9,2-10,2-11,2-12,2-13,2-14,"
            + "2-15,4-4,4-5,4-6,4-29,4-30,5-1,6-10,6-11,"
            + "6-12,9-19,9-20,9-21,10-1,10-2,10-3,10-4,10-5,10-6,10-7";
    //节假日前后的加班
    private final String isPlusDau = "10-8";

    //工作时间
    private final int morningBegin = 8;//上午开始时间 8点
    private final int morningEnd   = 12;//上午结束时间 12点

    private final int    afternoonBegin = 14;//下午开始时间 14点
    private final int    afternoonEnd   = 18;//结束时间   18点
    private final Logger logger         = Logger.getLogger(DateCal.class);

    /**
     * @param args
     */
    public static void main(String[] args)
    {
        try
        {
            String           strDateStart = "2018-07-06 16:00:00";
            String           strDateEnd   = "2018-07-09 16:40:10";
            SimpleDateFormat sdf          = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date             date_start   = sdf.parse(strDateStart);
            Date             date_end     = sdf.parse(strDateEnd);


            DateCal  app       = new DateCal();
            Calendar cal_start = Calendar.getInstance();
            Calendar cal_end   = Calendar.getInstance();
            cal_start.setTime(date_start);
            cal_end.setTime(date_end);

            System.out.println(app.getPluseHourDate(date_start,15.5));
            System.out.println(app.getMinusofTowDate(date_start,cal_end));
        }
        catch (Exception e)
        {
            // TODO: handle exception
        }
    }

    
    //判断是否是法定节假日
    private boolean isHodliDays(Calendar d1)
    {
        /*
        关于法定节假日,这里还有一个问题需要提前考虑下
        例如:
        国庆10月1号到10月7号,那10月8号正式上班,但是如果10月8号是周日,在计算的时候,也会不考虑进去。这就比较尴尬。
        另外就是10月8号是不是周末,不同年份又不同。
        处理办法:
        把可能产生上述问题的日期,都单独拎出来,在计算的时候,先判断是否满足这些日期,再判断这些日期是否是周末
        如果不是周末,正常计算,如果是周末,还要加上当天的时间。
         */
        String str = String.valueOf(d1.get(Calendar.MONTH) + 1) + "-" + String.valueOf(d1.get(Calendar.DAY_OF_MONTH));
        if (isHoliday.contains(str))
        {
            return true;
        }
        return false;
    }

    //判断是否是加班日
    private boolean isPlusDay(Calendar d1)
    {
        String str = String.valueOf(d1.get(Calendar.MONTH) + 1) + "-" + String.valueOf(d1.get(Calendar.DAY_OF_MONTH));
        if (isPlusDau.contains(str))
        {
            return true;
        }
        return false;
    }

    //判断是否是周末
    private boolean isWeek(Calendar d1)
    {
        if (d1.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || d1.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)
        {
            return true;
        }
        return false;
    }

    //预处理开始时间
    private Calendar getBeginDay(Calendar d1)
    {
        if (isHodliDays(d1))
        {
            //如果是节假日,往后延一天,并且从这一天的00:00:00开始
            d1 = addCalendar(d1, 1);
            return getBeginDay(d1);
        }
        else
        {
            //再判断是否是周末,如果是周末,判断是否是加班日
            if (isWeek(d1))
            {
                if (!isPlusDay(d1))
                {
                    d1 = addCalendar(d1, 1);
                    return getBeginDay(d1);
                }
                else
                {
                    return d1;
                }
            }
            else
            {
                return d1;
            }
        }
    }

    //预处理结束时间
    private Calendar getEndDay(Calendar d2)
    {
        if (isHodliDays(d2))
        {
            //如果是节假日,往前提一天,并且从这一天的00:00:00开始
            d2 = addCalendar(d2, -1);
            return getEndDay(d2);
        }
        else
        {
            //不是节假日
            if (isWeek(d2))
            {
                if (!isPlusDay(d2))
                {
                    d2 = addCalendar(d2, -1);
                    return getEndDay(d2);
                }
                else
                {
                    return d2;
                }
            }
            else
            {
                return d2;
            }
        }
    }

    //预处理重置时分秒
    private Calendar addCalendar(Calendar d, int m)
    {
        if (m == 1)
        {
            d.add(Calendar.DATE, 1);
            d.set(Calendar.HOUR_OF_DAY, 0);//也可直接设置为beginHour
            d.set(Calendar.MINUTE, 0);
            d.set(Calendar.SECOND, 0);
        }
        else
        {
            d.add(Calendar.DATE, -1);
            d.set(Calendar.HOUR_OF_DAY, 23);//也可直接设置为endHour
            d.set(Calendar.MINUTE, 59);
            d.set(Calendar.SECOND, 59);
        }
        return d;
    }

    //获取当天实际的工作时间
    private long getDayMiLLI(Calendar d, boolean isBegin)
    {
        long rv = 0;
        int  h  = d.get(Calendar.HOUR_OF_DAY);
        if (isBegin)
        {
            if (h < afternoonEnd)
            {
                if (h >= morningBegin)
                {
                    if (h >= afternoonBegin)
                    {
                        //计算开始那一天的时间长度
                        rv += (afternoonEnd - h) * 60 * 60 * 1000;
                        rv -= d.get(Calendar.MINUTE) * 60 * 1000;
                        rv -= d.get(Calendar.SECOND) * 1000;
                        rv -= d.get(Calendar.MILLISECOND);
                    }
                    else if (h >= morningEnd && h < afternoonBegin)
                    {
                        rv += (afternoonEnd - afternoonBegin) * 60 * 60 * 1000;
                    }
                    else
                    {
                        rv += ((afternoonEnd - afternoonBegin) + (morningEnd - h)) * 60 * 60 * 1000;
                        rv -= d.get(Calendar.MINUTE) * 60 * 1000;
                        rv -= d.get(Calendar.SECOND) * 1000;
                        rv -= d.get(Calendar.MILLISECOND);
                    }
                }
                else
                {
                    rv = ((afternoonEnd - afternoonBegin) + (morningEnd - morningBegin)) * 60 * 60 * 1000;
                }
            }
        }
        else
        {
            if (h >= morningBegin)
            {
                if (h < afternoonEnd)
                {
                    if (h >= afternoonBegin)
                    {
                        rv += ((morningEnd - morningBegin) + (h - afternoonBegin)) * 60 * 60 * 1000;
                        rv += d.get(Calendar.MINUTE) * 60 * 1000;
                        rv += d.get(Calendar.SECOND) * 1000;
                        rv += d.get(Calendar.MILLISECOND);
                    }
                    else if (h >= morningEnd && h < afternoonBegin)
                    {
                        rv += (morningEnd - morningBegin) * 60 * 60 * 1000;
                    }
                    else
                    {
                        rv += (h - morningBegin) * 60 * 60 * 1000;
                        rv += d.get(Calendar.MINUTE) * 60 * 1000;
                        rv += d.get(Calendar.SECOND) * 1000;
                        rv += d.get(Calendar.MILLISECOND);
                    }
                }
                else
                {
                    rv = ((afternoonEnd - afternoonBegin) + (morningEnd - morningBegin)) * 60 * 60 * 1000;
                }
            }
        }
        return rv;
    }

    //核心计算函数,返回毫秒
    private long getDayMiLLI(Calendar c1, Calendar c2)
    {
        long beginL = 0;//开始天时间
        long endL   = 0;//结束天时间
        long rv     = 0;
        //如果开始日期和结束日期不是同一天,开始往前计算
        if (c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) || c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) || c1.get(Calendar.DAY_OF_MONTH) != c2
                .get(Calendar.DAY_OF_MONTH))
        {
            //不是同一天
            beginL = getDayMiLLI(c1, true);
            endL = getDayMiLLI(c2, false);
            rv = beginL + endL;
            c1.add(Calendar.DATE, 1);
            while (c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) || c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) || c1
                    .get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH))
            {
                if (!isHodliDays(c1))
                {
                    if (!isWeek(c1))
                    {
                        rv += ((afternoonEnd - afternoonBegin) + (morningEnd - morningBegin)) * 60 * 60 * 1000;
                    }
                    else
                    {
                        if (isPlusDay(c1))
                        {
                            rv += ((afternoonEnd - afternoonBegin) + (morningEnd - morningBegin)) * 60 * 60 * 1000;
                        }
                    }
                }
                c1.add(Calendar.DATE, 1);
            }
        }
        else
        {
            //是同一天
            int bh = c1.get(Calendar.HOUR_OF_DAY);//开始
            int eh = c2.get(Calendar.HOUR_OF_DAY);//结束
            if (bh < morningBegin)
            {
                //开始时间小于早上开始时间,就等于说及计算结束时间在这一天的实际时间
                rv += getDayMiLLI(c2, false);
            }
            else
            {
                if (eh >= afternoonEnd)
                {
                    //结束时间大于下午结束时间,等于就是计算开始时间在这一天的实际时间
                    rv += getDayMiLLI(c1, true);
                }
                else
                {
                    /**
                     * 开始和结束都在中间时间段
                     * 1.开始和结束都在上午
                     * 2.开始和结束都在下午
                     * 3.开始在上午,结束在下午
                     * 4.开始在中间,结束不在
                     * 5.结束在中间,开始不在
                     * 6.开始和结束都在中间
                     */

                    if (eh < morningEnd || bh >= afternoonBegin)
                    {
                        //都在上午或者都在下午
                        rv += (eh - bh) * 60 * 60 * 1000;
                        rv -= c1.get(Calendar.MINUTE) * 60 * 1000;
                        rv -= c1.get(Calendar.SECOND) * 1000;
                        rv -= c1.get(Calendar.MILLISECOND);
                        rv += c2.get(Calendar.MINUTE) * 60 * 1000;
                        rv += c2.get(Calendar.SECOND) * 1000;
                        rv += c2.get(Calendar.MILLISECOND);
                    }
                    else if (bh < morningEnd && eh >= afternoonBegin)
                    {
                        //开始在上午,结束在下午
                        rv += ((eh - bh) - (afternoonBegin - morningEnd)) * 60 * 60 * 1000;
                        rv -= c1.get(Calendar.MINUTE) * 60 * 1000;
                        rv -= c1.get(Calendar.SECOND) * 1000;
                        rv -= c1.get(Calendar.MILLISECOND);
                        rv += c2.get(Calendar.MINUTE) * 60 * 1000;
                        rv += c2.get(Calendar.SECOND) * 1000;
                        rv += c2.get(Calendar.MILLISECOND);
                    }
                    else if (bh < morningEnd && eh < afternoonBegin)
                    {
                        //开始在上午,结束在中间
                        rv += (morningEnd - bh) * 60 * 60 * 1000;
                        rv -= c1.get(Calendar.MINUTE) * 60 * 1000;
                        rv -= c1.get(Calendar.SECOND) * 1000;
                        rv -= c1.get(Calendar.MILLISECOND);
                    }
                    else if (bh > morningEnd && eh >= afternoonBegin)
                    {
                        //开始在中间,结束在下午
                        rv += (eh - afternoonBegin) * 60 * 60 * 1000;
                        rv += c2.get(Calendar.MINUTE) * 60 * 1000;
                        rv += c2.get(Calendar.SECOND) * 1000;
                        rv += c2.get(Calendar.MILLISECOND);
                    }
                    else
                    {
                        logger.debug("the begin time c1 " + c1.toString() + " and the end time c2 " + c2.toString() + " in not work day!");
                    }
                }
            }
        }
        return rv;
    }

    private Calendar getNew(Calendar begin)
    {
        begin.add(Calendar.DATE, 1);
        begin.set(Calendar.HOUR_OF_DAY, morningBegin);
        begin.set(Calendar.MINUTE, 0);
        begin.set(Calendar.SECOND, 0);
        return begin;
    }

    //核心计算函数,返回日期
    private Date func(Calendar begin, double hours)
    {
        begin = getBeginDay(begin);
        int h = begin.get(Calendar.HOUR_OF_DAY);
        if (h < morningBegin)
        {
            //全天
            if (hours > ((afternoonEnd - afternoonBegin) + (morningEnd - morningBegin)))
            {
                begin = getNew(begin);
                hours = hours - ((afternoonEnd - afternoonBegin) + (morningEnd - morningBegin));
                return func(begin, hours);
            }
            else
            {
                /**
                 * 这里要判断下,这个小时是否大于上午的工作时间,大于的话,时间就的到下午去了
                 */
                if (hours > (morningEnd - morningBegin))
                {
                    begin.set(Calendar.HOUR_OF_DAY, afternoonBegin);
                    hours = hours - (morningEnd - morningBegin);
                }
                else
                {
                    begin.set(Calendar.HOUR_OF_DAY, morningBegin);
                }
                begin.set(Calendar.MINUTE, 0);
                begin.set(Calendar.SECOND, 0);
                begin.set(Calendar.MILLISECOND, 0);
                long time = begin.getTime().getTime();
                hours = hours * 60 * 60 * 1000;
                time += hours;
                return new Date(time);
            }
        }
        else if (h >= afternoonEnd)
        {
            //过期,新增一天,重新算
            begin.add(Calendar.DATE, 1);
            return func(begin, hours);
        }
        else
        {
            //计算
            long   tm      = getDayMiLLI(begin, true);//今天的时间
            double houts_m = hours * 60 * 60 * 1000;
            if (tm >= houts_m)
            {
                //不跨天,计算今天的
                if (h < morningEnd)
                {
                    //在上午
                    long rv = 0;
                    rv += (morningEnd - h) * 60 * 60 * 1000;
                    rv -= begin.get(Calendar.MINUTE) * 60 * 1000;
                    rv -= begin.get(Calendar.SECOND) * 1000;
                    rv -= begin.get(Calendar.MILLISECOND);
                    if (houts_m > rv)
                    {
                        //到下午
                        begin.set(Calendar.HOUR_OF_DAY, afternoonBegin);
                        begin.set(Calendar.MINUTE, 0);
                        begin.set(Calendar.SECOND, 0);
                        begin.set(Calendar.MILLISECOND, 0);
                        long time = begin.getTime().getTime();
                        time += (houts_m - rv);
                        return new Date(time);
                    }
                    else
                    {
                        //还在上午
                        long time = begin.getTime().getTime();
                        time += houts_m;
                        return new Date(time);
                    }
                }
                else if (h >= afternoonBegin)
                {
                    //在下午
                    long time = begin.getTime().getTime();
                    time += houts_m;
                    return new Date(time);
                }
                else
                {
                    //在中间
                    begin.set(Calendar.HOUR_OF_DAY, afternoonBegin);
                    begin.set(Calendar.MINUTE, 0);
                    begin.set(Calendar.SECOND, 0);
                    begin.set(Calendar.MILLISECOND, 0);
                    long time = begin.getTime().getTime();
                    time += houts_m;
                    return new Date(time);
                }
            }
            else
            {
                //跨天,到第二天
                begin = getNew(begin);
                hours = (houts_m - tm) / 1000 / 60 / 60;
                return func(begin, hours);
            }
        }
    }

    /**
     * 计算两个日期之间的时间差,不算节假日、不算周末、不算非正常工作时间
     * <p>
     * 设计思路:
     * 取开始时间,判断是否是节假日、是否是周末、是否是加班日
     * 如果是节假日:
     * 往后+1天,再继续判断
     * 如果是周末:
     * 判断是否是加班日,如果不是加班日,往后+1天
     *
     * @param begin 开始时间
     * @param end   结束时间
     * @return 时间差,单位是毫秒
     */
    public long getMinusofTowDate(Calendar begin, Calendar end)
    {
        long midL = 0;//中间差值时间
        // 0.预处理
        begin = getBeginDay(begin);
        end = getEndDay(end);
        // 1.如果开始时间大于结束时间,交换下,同时结果返回负数即可
        if (begin.after(end))
        {
            Calendar swap = begin;
            begin = end;
            end = swap;
            midL = -getDayMiLLI(begin, end);
        }
        else
        {
            midL = getDayMiLLI(begin, end);
        }
        // 2.计算开始
        return midL;
    }

    /**
     * 根据日期计算xx小时后对应日期,刨除工作日、节假日
     *
     * @param d
     * @param hours
     * @return
     */
    public Date getPluseHourDate(Date d, double hours)
    {
        Calendar cal_start = Calendar.getInstance();
        cal_start.setTime(d);
        return func(cal_start, hours);
    }
}

注:

DateCal作为一个单独的工具类提了出来,对外提供的两个方法:

public long getMinusofTowDate(Calendar begin, Calendar end)
public Date getPluseHourDate(Date d, double hours)

优化点:

可以将法定节假日、是否加班日、工作时间这些变量作为配置数据从数据库中或者从配置文件中读取,这样在发生变化和更新的时候,不用重新编译发布,方便快捷。

 

20210219更新

代码做了部分优化和调整,调整点如下:

(1)上班时间支持自定义设置,包括上午开始时间、下班时间以及下午的上班时间及下班时间

(2)节假日及节假日前后的加班,提到外置,可以通过配置或者读取数据库的方式

优化后的工具类代码如下:

package com.tydic.utils;

import org.apache.log4j.Logger;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;


public class DateCal
{
    //法定节假日
    private final String isHoliday = "1-1,1-2,1-3,2-9,2-10,2-11,2-12,2-13,2-14," +
            "2-15,4-4,4-5,4-6,4-29,4-30,5-1,6-10,6-11," +
            "6-12,9-24,10-1,10-2,10-3,10-4,10-5,10-6,10-7";
    //节假日前后的加班
    private final String isPlusDau = "10-8";

    //工作时间
    private int morningBegin    = 8;//上午开始时间 8点
    private int morningBeginMin = 30;//分钟 0分钟
    private int morningEnd      = 12;//上午结束时间 12点
    private int morningEndMin   = 0;//分钟 0分钟

    private int afternoonBegin    = 14;//下午开始时间 14点
    private int afternoonBeginMin = 30;//分钟 14点
    private int afternoonEnd      = 18;//下午结束时间   18点
    private int afternoonEndMin   = 0;//分钟   18点

    private Logger logger = Logger.getLogger(DateCal.class);

    public DateCal(int morningBegin, int morningBeginMin, int morningEnd, int morningEndMin, int afternoonBegin, int afternoonBeginMin, int afternoonEnd, int afternoonEndMin)
    {
        this.morningBegin = morningBegin;
        this.morningBeginMin = morningBeginMin;
        this.morningEnd = morningEnd;
        this.morningEndMin = morningEndMin;
        this.afternoonBegin = afternoonBegin;
        this.afternoonBeginMin = afternoonBeginMin;
        this.afternoonEnd = afternoonEnd;
        this.afternoonEndMin = afternoonEndMin;
    }

    public static void main(String[] args)
    {
        try
        {
            String           strDateStart = "2020-01-02 12:35:59";
            String           strDateEnd   = "2020-01-02 12:36:21";
            SimpleDateFormat sdf          = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date             date_start   = sdf.parse(strDateStart);
            Date             date_end     = sdf.parse(strDateEnd);

            DateCal  app       = new DateCal(8,30,12,0,14,30,18,0);
            Calendar cal_start = Calendar.getInstance();
            Calendar cal_end   = Calendar.getInstance();
            cal_start.setTime(date_start);
            cal_end.setTime(date_end);
            //System.out.println(DateUtil.getDate(app.getPluseHourDate(date_start,2),DateUtil.TYPE4));
            System.out.println(app.getMinusofTowDate(cal_start, cal_end));
        }
        catch (Exception e)
        {
            // TODO: handle exception
        }
    }

    //判断是否是法定节假日
    private boolean isHodliDays(Calendar d1)
    {
        /*
        关于法定节假日,这里还有一个问题需要提前考虑下
        例如:
        国庆10月1号到10月7号,那10月8号正式上班,但是如果10月8号是周日,在计算的时候,也会不考虑进去。这就比较尴尬。
        另外就是10月8号是不是周末,不同年份又不同。
        处理办法:
        把可能产生上述问题的日期,都单独拎出来,在计算的时候,先判断是否满足这些日期,再判断这些日期是否是周末
        如果不是周末,正常计算,如果是周末,还要加上当天的时间。
         */
        String str = String.valueOf(d1.get(Calendar.MONTH) + 1) + "-" + String.valueOf(d1.get(Calendar.DAY_OF_MONTH));
        // if (isHoliday.contains(str))
        if (CommTool.getHoidayDay().contains(str))
        {
            return true;
        }
        return false;
    }

    //判断是否是加班日
    private boolean isPlusDay(Calendar d1)
    {
        String str = String.valueOf(d1.get(Calendar.MONTH) + 1) + "-" + String.valueOf(d1.get(Calendar.DAY_OF_MONTH));
        //if (isPlusDau.contains(str))
        if (CommTool.getPlusDay().contains(str))
        {
            return true;
        }
        return false;
    }

    //判断是否是周末
    private boolean isWeek(Calendar d1)
    {
        if (d1.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || d1.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)
        {
            return true;
        }
        return false;
    }

    //预处理开始时间
    private Calendar getBeginDay(Calendar d1)
    {
        if (isHodliDays(d1))
        {
            //如果是节假日,往后延一天,并且从这一天的00:00:00开始
            d1 = addCalendar(d1, 1);
            return getBeginDay(d1);
        }
        else
        {
            //再判断是否是周末,如果是周末,判断是否是加班日
            if (isWeek(d1))
            {
                if (!isPlusDay(d1))
                {
                    d1 = addCalendar(d1, 1);
                    return getBeginDay(d1);
                }
                else
                {
                    return d1;
                }
            }
            else
            {
                return d1;
            }
        }
    }

    //预处理结束时间
    private Calendar getEndDay(Calendar d2)
    {
        if (isHodliDays(d2))
        {
            //如果是节假日,往前提一天,并且从这一天的00:00:00开始
            d2 = addCalendar(d2, -1);
            return getEndDay(d2);
        }
        else
        {
            //不是节假日
            if (isWeek(d2))
            {
                if (!isPlusDay(d2))
                {
                    d2 = addCalendar(d2, -1);
                    return getEndDay(d2);
                }
                else
                {
                    return d2;
                }
            }
            else
            {
                return d2;
            }
        }
    }

    //预处理重置时分秒
    private Calendar addCalendar(Calendar d, int m)
    {
        if (m == 1)
        {
            d.add(Calendar.DATE, 1);
            d.set(Calendar.HOUR_OF_DAY, 0);//也可直接设置为beginHour
            d.set(Calendar.MINUTE, 0);
            d.set(Calendar.SECOND, 0);
        }
        else
        {
            d.add(Calendar.DATE, -1);
            d.set(Calendar.HOUR_OF_DAY, 23);//也可直接设置为endHour
            d.set(Calendar.MINUTE, 59);
            d.set(Calendar.SECOND, 59);
        }
        return d;
    }

    //获取当天实际的工作时间
    private long getDayMiLLI(Calendar d, boolean isBegin)
    {
        long rv = 0;
        int  h  = d.get(Calendar.HOUR_OF_DAY);//时
        int  m  = d.get(Calendar.MINUTE);//分
        if (isBegin)//开始时间取d,结束时间默认设置的下午下班时间,算一天
        {
            if (h < afternoonEnd)
            {
                if (h >= morningBegin)
                {
                    if (h >= afternoonBegin)
                    {
                        //开始时间在下午
                        rv += (afternoonEnd - h) * 60 * 60 * 1000;
                        rv += afternoonEndMin * 60 * 1000;

                        rv -= d.get(Calendar.MINUTE) * 60 * 1000;
                        rv -= d.get(Calendar.SECOND) * 1000;
                        rv -= d.get(Calendar.MILLISECOND);

                        if (h == afternoonBegin && m < afternoonBeginMin)
                        {
                            rv += d.get(Calendar.MINUTE) * 60 * 1000;
                            rv += d.get(Calendar.SECOND) * 1000;
                            rv += d.get(Calendar.MILLISECOND);
                            rv -= afternoonBeginMin * 60 * 1000;
                        }
                    }
                    else if (h >= morningEnd && h < afternoonBegin)
                    {
                        //开始时间在中午午休那一段,考虑上午结束的时分秒
                        rv += (afternoonEnd - afternoonBegin) * 60 * 60 * 1000;
                        rv += afternoonEndMin * 60 * 1000;
                        rv -= afternoonBeginMin * 60 * 1000;

                        if (h == morningEnd && m < morningEndMin)
                        {
                            rv += (morningEndMin - m) * 60 * 1000;
                            rv -= d.get(Calendar.SECOND) * 1000;
                            rv -= d.get(Calendar.MILLISECOND);
                        }
                    }
                    else
                    {
                        rv += ((afternoonEnd - afternoonBegin) + (morningEnd - h)) * 60 * 60 * 1000;
                        rv -= afternoonBeginMin * 60 * 1000;
                        rv += afternoonEndMin * 60 * 1000;
                        rv += morningEndMin * 60 * 1000;

                        rv -= d.get(Calendar.MINUTE) * 60 * 1000;
                        rv -= d.get(Calendar.SECOND) * 1000;
                        rv -= d.get(Calendar.MILLISECOND);

                        if (h == morningBegin && m < morningBeginMin)
                        {
                            rv += d.get(Calendar.MINUTE) * 60 * 1000;
                            rv += d.get(Calendar.SECOND) * 1000;
                            rv += d.get(Calendar.MILLISECOND);
                            rv -= morningBeginMin * 60 * 1000;
                        }
                    }
                }
                else
                {
                    //这就计算一整天就行了,因为开始时间在早上上班之间之前
                    rv = getOneDayMins();
                }
            }
            else if (h == afternoonEnd)
            {
                if (m < afternoonEndMin)
                {
                    rv += (afternoonEnd - m) * 60 * 1000;
                    rv -= d.get(Calendar.SECOND) * 1000;
                    rv -= d.get(Calendar.MILLISECOND);
                }
            }
        }
        else
        {
            //开始时间取默认上午上班时间,结束时间取d,算一天
            if (h > morningBegin) //大于等于开始时间,才会去算工作时间
            {
                if (h < afternoonEnd)
                {
                    if (h >= afternoonBegin)
                    {
                        //结束时间在下午工作时间段
                        //先按整点算
                        rv += ((morningEnd - morningBegin) + (h - afternoonBegin)) * 60 * 60 * 1000;
                        rv -= morningBeginMin * 60 * 1000;//开始分钟减去
                        rv += morningEndMin * 60 * 1000;//结束分钟加上
                        if (h == afternoonBegin)
                        {
                            if (m >= afternoonBeginMin)
                            {
                                rv += (m - afternoonBeginMin) * 60 * 1000;
                                rv += d.get(Calendar.SECOND) * 1000;
                                rv += d.get(Calendar.MILLISECOND);
                            }
                        }
                        else
                        {
                            rv -= afternoonBeginMin * 60 * 1000;
                            rv += m * 60 * 1000;
                            rv += d.get(Calendar.SECOND) * 1000;
                            rv += d.get(Calendar.MILLISECOND);
                        }
                    }
                    else if (h >= morningEnd && h < afternoonBegin)
                    {
                        rv += (morningEnd - morningBegin) * 60 * 60 * 1000;
                        rv -= morningBeginMin * 60 * 1000;
                        rv += morningEndMin * 60 * 1000;
                        if (h == morningEnd)
                        {
                            if (m < morningEndMin)
                            {
                                rv -= morningEndMin * 60 * 1000;
                                rv += m * 60 * 1000;
                                rv += d.get(Calendar.SECOND) * 1000;
                                rv += d.get(Calendar.MILLISECOND);
                            }
                        }
                    }
                    else
                    {
                        rv += (h - morningBegin) * 60 * 60 * 1000;
                        rv -= morningBeginMin * 60 * 1000;
                        rv += d.get(Calendar.MINUTE) * 60 * 1000;
                        rv += d.get(Calendar.SECOND) * 1000;
                        rv += d.get(Calendar.MILLISECOND);
                    }
                }
                else
                {
                    rv = ((afternoonEnd - afternoonBegin) + (morningEnd - morningBegin)) * 60 * 60 * 1000;
                    rv -= morningBeginMin * 60 * 1000;//开始分钟减去
                    rv += morningEndMin * 60 * 1000;//结束分钟加上
                    rv -= afternoonBeginMin * 60 * 1000;//开始分钟减去
                    rv += afternoonEndMin * 60 * 1000;//结束分钟加上
                    if (h == afternoonEnd && m < afternoonEndMin)
                    {
                        rv -= afternoonEndMin * 60 * 1000;
                        rv += m * 60 * 1000;
                        rv += d.get(Calendar.SECOND) * 1000;
                        rv += d.get(Calendar.MILLISECOND);
                    }
                }
            }
            else if (h == morningBegin)
            {
                if (m >= morningBeginMin)
                {
                    rv += (m - morningBeginMin) * 60 * 1000;
                    rv += d.get(Calendar.SECOND) * 1000;
                    rv += d.get(Calendar.MILLISECOND);
                }
            }
        }
        return rv;
    }

    //核心计算函数,返回毫秒
    private long getDayMiLLI(Calendar c1, Calendar c2)
    {
        long beginL = 0;//开始天时间
        long endL   = 0;//结束天时间
        long rv     = 0;
        //如果开始日期和结束日期不是同一天,开始往前计算
        if (c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) || c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) || c1.get(Calendar.DAY_OF_MONTH) != c2
                .get(Calendar.DAY_OF_MONTH))
        {
            //不是同一天,开始日期往前加一天,rv时间累积一天的工作时间
            beginL = getDayMiLLI(c1, true);
            endL = getDayMiLLI(c2, false);
            rv = beginL + endL;
            c1.add(Calendar.DATE, 1);
            while (c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) || c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) || c1
                    .get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH))
            {
                if (!isHodliDays(c1)) //不是节假日
                {
                    //如果不是周末或者是加班日
                    if (!isWeek(c1) || isPlusDay(c1))
                    {
                        rv += getOneDayMins();
                    }
                }
                c1.add(Calendar.DATE, 1);
            }
        }
        else
        {
            //是同一天
            int bh = c1.get(Calendar.HOUR_OF_DAY);//开始小时
            int m1 = c1.get(Calendar.MINUTE);

            int eh = c2.get(Calendar.HOUR_OF_DAY);//结束小时
            int m2 = c2.get(Calendar.MINUTE);
            if (bh <= morningBegin)
            {
                //开始时间小于早上开始时间,就等于说及计算结束时间在这一天的实际时间
                rv += getDayMiLLI(c2, false);
                if (bh == morningBegin)
                {
                    if (m1 >= morningBeginMin)
                    {
                        rv -= (m1 - morningBeginMin) * 60 * 1000;
                        rv -= c1.get(Calendar.SECOND) * 1000;
                        rv -= c1.get(Calendar.MILLISECOND);
                    }
                }
            }
            else
            {
                if (eh >= afternoonEnd)
                {
                    //结束时间大于下午结束时间,等于就是计算开始时间在这一天的实际时间
                    rv += getDayMiLLI(c1, true);
                    if (eh == afternoonEnd)
                    {
                        if (m2 < afternoonEndMin)
                        {
                            rv -= (afternoonEndMin - m2) * 60 * 1000;
                            rv += c1.get(Calendar.SECOND) * 1000;
                            rv += c1.get(Calendar.MILLISECOND);
                        }
                    }
                }
                else
                {
                    /**
                     * 开始和结束都在中间时间段
                     * 1.开始和结束都在上午
                     * 2.开始和结束都在下午
                     * 3.开始在上午,结束在下午
                     * 4.开始在中间,结束不在
                     * 5.结束在中间,开始不在
                     * 6.开始和结束都在中间
                     */

                    if (eh <= morningEnd || bh >= afternoonBegin)
                    {
                        //都在上午或者都在下午
                        rv += (eh - bh) * 60 * 60 * 1000;
                        rv -= c1.get(Calendar.MINUTE) * 60 * 1000;
                        rv -= c1.get(Calendar.SECOND) * 1000;
                        rv -= c1.get(Calendar.MILLISECOND);
                        rv += c2.get(Calendar.MINUTE) * 60 * 1000;
                        rv += c2.get(Calendar.SECOND) * 1000;
                        rv += c2.get(Calendar.MILLISECOND);
                        if (bh == afternoonBegin)
                        {
                            if(eh==afternoonBegin && m2<afternoonBeginMin){
                                rv=0;
                            }
                            else if (m1 < afternoonBeginMin)
                            {
                                rv += c1.get(Calendar.MINUTE) * 60 * 1000;
                                rv += c1.get(Calendar.SECOND) * 1000;
                                rv += c1.get(Calendar.MILLISECOND);
                                rv -= afternoonBeginMin * 60 * 1000;
                            }
                        }
                        if (eh == morningEnd)
                        {
                            if(bh == morningEnd && m1>=morningEndMin){
                                    rv=0;
                            }
                            else if (m2 >= morningEndMin)
                            {
                                rv -= c2.get(Calendar.MINUTE) * 60 * 1000;
                                rv -= c2.get(Calendar.SECOND) * 1000;
                                rv -= c2.get(Calendar.MILLISECOND);
                                rv += morningEndMin * 60 * 1000;
                            }
                        }
                    }
                    else if (bh <= morningEnd && eh >= afternoonBegin)
                    {
                        //开始在上午,结束在下午
                        rv += ((eh - bh) - (afternoonBegin - morningEnd)) * 60 * 60 * 1000;
                        rv -= c1.get(Calendar.MINUTE) * 60 * 1000;
                        rv -= c1.get(Calendar.SECOND) * 1000;
                        rv -= c1.get(Calendar.MILLISECOND);
                        rv += c2.get(Calendar.MINUTE) * 60 * 1000;
                        rv += c2.get(Calendar.SECOND) * 1000;
                        rv += c2.get(Calendar.MILLISECOND);

                        rv += morningEndMin * 60 * 1000;
                        rv -= afternoonBeginMin * 60 * 1000;
                        if (eh == afternoonBegin)
                        {
                            if (m2 < afternoonBeginMin)
                            {
                                rv += afternoonBeginMin * 60 * 1000;
                                rv -= c2.get(Calendar.MINUTE) * 60 * 1000;
                                rv -= c2.get(Calendar.SECOND) * 1000;
                                rv -= c2.get(Calendar.MILLISECOND);
                            }
                        }
                        if (bh == morningEnd)
                        {
                            if (m1 >= morningEndMin)
                            {
                                rv += c1.get(Calendar.MINUTE) * 60 * 1000;
                                rv += c1.get(Calendar.SECOND) * 1000;
                                rv += c1.get(Calendar.MILLISECOND);
                                rv -= morningEndMin * 60 * 1000;
                            }
                        }
                    }
                    else if (bh <= morningEnd && eh < afternoonBegin)
                    {
                        //开始在上午,结束在中间
                        rv += (morningEnd - bh) * 60 * 60 * 1000;
                        rv -= c1.get(Calendar.MINUTE) * 60 * 1000;
                        rv -= c1.get(Calendar.SECOND) * 1000;
                        rv -= c1.get(Calendar.MILLISECOND);
                        rv += morningEndMin * 60 * 1000;
                        if (bh == morningEnd)
                        {
                            if (m1 >= morningEndMin)
                            {
                                rv += c1.get(Calendar.MINUTE) * 60 * 1000;
                                rv += c1.get(Calendar.SECOND) * 1000;
                                rv += c1.get(Calendar.MILLISECOND);
                                rv -= morningEndMin * 60 * 1000;
                            }
                        }
                    }
                    else if (bh > morningEnd && eh >= afternoonBegin)
                    {
                        //开始在中间,结束在下午
                        if (eh > afternoonBegin)
                        {
                            rv += (eh - afternoonBegin) * 60 * 60 * 1000;
                            rv += c2.get(Calendar.MINUTE) * 60 * 1000;
                            rv += c2.get(Calendar.SECOND) * 1000;
                            rv += c2.get(Calendar.MILLISECOND);
                        }
                        else
                        {
                            if (m2 >= afternoonBeginMin)
                            {
                                rv += (m2 - afternoonBeginMin) * 60 * 1000;
                                rv += c2.get(Calendar.SECOND) * 1000;
                                rv += c2.get(Calendar.MILLISECOND);
                            }
                        }
                    }
                    else
                    {
                        logger.debug("the begin time c1 " + c1.toString() + " and the end time c2 " + c2.toString() + " in not work day!");
                    }
                }
            }
        }
        return rv;
    }

    private Calendar getNew(Calendar begin)
    {
        begin.add(Calendar.DATE, 1);
        begin.set(Calendar.HOUR_OF_DAY, morningBegin);
        begin.set(Calendar.MINUTE, morningBeginMin);
        begin.set(Calendar.SECOND, 0);
        return begin;
    }

    //获取一天的实际上班时间
    private int getOneDayMins()
    {
        int rv = 0;
        rv += ((afternoonEnd - afternoonBegin) + (morningEnd - morningBegin)) * 60 * 60 * 1000;
        rv -= morningBeginMin * 60 * 1000;//开始分钟减去
        rv += morningEndMin * 60 * 1000;//结束分钟加上
        rv -= afternoonBeginMin * 60 * 1000;//开始分钟减去
        rv += afternoonEndMin * 60 * 1000;//结束分钟加上
        System.out.println("---->" + rv);
        return rv;
    }

    //核心计算函数,返回日期
    private Date func(Calendar begin, double hours)
    {
        begin = getBeginDay(begin);
        int h = begin.get(Calendar.HOUR_OF_DAY);
        int m = begin.get(Calendar.MINUTE);
        if (h < morningBegin)
        {
            //全天
            int o_min = getOneDayMins();
            if (hours > o_min)
            {
                begin = getNew(begin);
                hours = hours - o_min;
                return func(begin, hours);
            }
            else
            {
                /**
                 * 这里要判断下,这个小时是否大于上午的工作时间,大于的话,时间就的到下午去了
                 */
                int morning_min = (morningEnd - morningBegin) * 60 * 60 * 1000;
                morning_min -= morningBeginMin * 60 * 1000;
                morning_min += morningEndMin * 60 * 1000;
                if (hours > morning_min)
                {
                    begin.set(Calendar.HOUR_OF_DAY, afternoonBegin);
                    begin.set(Calendar.MINUTE, afternoonBeginMin);
                    hours = hours - morning_min;
                }
                else
                {
                    begin.set(Calendar.HOUR_OF_DAY, morningBegin);
                    begin.set(Calendar.MINUTE, morningBeginMin);
                }
                begin.set(Calendar.SECOND, 0);
                begin.set(Calendar.MILLISECOND, 0);
                long time = begin.getTime().getTime();
                time += hours;
                return new Date(time);
            }
        }
        else if (h >= afternoonEnd)
        {
            //过期,新增一天,重新算
            if (h == afternoonEnd && m < afternoonEndMin)
            {
                hours -= m * 60 * 1000;
                hours -= begin.get(Calendar.SECOND) * 1000;
                hours -= begin.get(Calendar.MILLISECOND);
            }
            begin = getNew(begin);
            return func(begin, hours);
        }
        else
        {
            //计算
            long   tm      = getDayMiLLI(begin, true);//今天的时间
            double houts_m = hours;
            if (tm >= houts_m)
            {
                //不跨天,计算今天的
                if (h < morningEnd)
                {
                    //在上午
                    long rv = 0;
                    rv += (morningEnd - h) * 60 * 60 * 1000;
                    rv += morningEndMin * 60 * 1000;
                    rv -= begin.get(Calendar.MINUTE) * 60 * 1000;
                    rv -= begin.get(Calendar.SECOND) * 1000;
                    rv -= begin.get(Calendar.MILLISECOND);

                    if (h == morningBegin)
                    {
                        if (m < morningBeginMin)
                        {
                            rv += begin.get(Calendar.MINUTE) * 60 * 1000;
                            rv += begin.get(Calendar.SECOND) * 1000;
                            rv += begin.get(Calendar.MILLISECOND);
                            rv -= morningBeginMin * 60 * 1000;
                            begin.set(Calendar.MINUTE, morningBeginMin);
                            begin.set(Calendar.SECOND, 0);
                            begin.set(Calendar.MILLISECOND, 0);
                        }
                    }

                    if (houts_m > rv)
                    {
                        //到下午
                        begin.set(Calendar.HOUR_OF_DAY, afternoonBegin);
                        begin.set(Calendar.MINUTE, afternoonBeginMin);
                        begin.set(Calendar.SECOND, 0);
                        begin.set(Calendar.MILLISECOND, 0);
                        long time = begin.getTime().getTime();
                        time += (houts_m - rv);
                        return new Date(time);
                    }
                    else
                    {
                        //还在上午
                        long time = begin.getTime().getTime();
                        time += houts_m;
                        return new Date(time);
                    }
                }
                else if (h >= afternoonBegin)
                {
                    if (h == afternoonBegin && m < afternoonBeginMin)
                    {
                        begin.set(Calendar.MINUTE, afternoonBeginMin);
                        begin.set(Calendar.SECOND, 0);
                        begin.set(Calendar.MILLISECOND, 0);
                    }
                    //在下午
                    long time = begin.getTime().getTime();
                    time += houts_m;
                    return new Date(time);
                }
                else
                {
                    //在中间
                    begin.set(Calendar.HOUR_OF_DAY, afternoonBegin);
                    begin.set(Calendar.MINUTE, afternoonBeginMin);
                    begin.set(Calendar.SECOND, 0);
                    begin.set(Calendar.MILLISECOND, 0);
                    long time = begin.getTime().getTime();
                    time += houts_m;
                    return new Date(time);
                }
            }
            else
            {
                //跨天,到第二天
                begin = getNew(begin);
                hours = houts_m - tm;
                return func(begin, hours);
            }
        }
    }

    /**
     * 计算两个日期之间的时间差,不算节假日、不算周末、不算非正常工作时间
     * <p>
     * 设计思路:
     * 取开始时间,判断是否是节假日、是否是周末、是否是加班日
     * 如果是节假日:
     * 往后+1天,再继续判断
     * 如果是周末:
     * 判断是否是加班日,如果不是加班日,往后+1天
     *
     * @param begin 开始时间
     * @param end   结束时间
     * @return 时间差,单位是毫秒
     */
    public long getMinusofTowDate(Calendar begin, Calendar end)
    {
        long midL = 0;//中间差值时间
        // 0.预处理
        begin = getBeginDay(begin);
        end = getEndDay(end);
        // 1.如果开始时间大于结束时间,交换下,同时结果返回负数即可
        if (begin.after(end))
        {
            Calendar swap = begin;
            begin = end;
            end = swap;
            midL = -getDayMiLLI(begin, end);
        }
        else
        {
            midL = getDayMiLLI(begin, end);
        }
        // 2.计算开始
        return midL;
    }

    /**
     * 根据日期计算xx小时后对应日期,刨除工作日、节假日
     *
     * @param d
     * @param hours
     * @return
     */
    public Date getPluseHourDate(Date d, double hours)
    {
        Calendar cal_start = Calendar.getInstance();
        cal_start.setTime(d);
        hours = hours * 60 * 60 * 1000;
        return func(cal_start, hours);
    }
}

 

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
计算时间差并排除周末工作时间节假日,可以使用以下步骤: 1. 使用DATEDIFF函数计算两个日期之间的天数。 2. 排除周末的天数,可以使用WEEKDAY函数,WEEKDAY返回0表示星期日,1表示星期一,以此类推。如果DATEDIFF的结果是7的倍数,则排除的天数应该为2倍的周末天数。 3. 排除工作时间的天数,可以使用HOUR函数计算两个日期之间的小时数,并将小时数转换为天数。将所得天数从总天数中减去。 4. 排除节假日的天数,可以使用子查询或JOIN操作,将日期表与节假日表JOIN,然后排除在节假日表中出现的日期。 下面是一个示例查询,假设要计算2021年1月1日到2021年1月31日之间的工作日天数: ``` SELECT DATEDIFF('2021-01-31', '2021-01-01') + 1 AS total_days, -- 计算总天数 SUM(CASE WHEN WEEKDAY(date) IN (5,6) THEN 2 ELSE 1 END) AS weekend_days, -- 计算周末天数 SUM(HOUR(TIMEDIFF('18:00:00', '09:00:00'))) / 24 AS off_hours_days, -- 计算工作时间天数 (SELECT COUNT(*) FROM holiday_table WHERE date BETWEEN '2021-01-01' AND '2021-01-31') AS holiday_days -- 计算节假日天数 FROM date_table WHERE date BETWEEN '2021-01-01' AND '2021-01-31'; ``` 其中,date_table是一个包所有日期的表,holiday_table是一个包所有节假日日期的表。查询结果将返回总天数、周末天数、工作时间天数和节假日天数。将这些天数从总天数中减去,即可得到实际的工作日天数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值