计算工作日天数-excel函数networkdays在java中的实现[简化版]

由于某个业务需要计算两个日期间的除去工作日、法定假日的工作日天数,然后想起来excel里面有个networkdays函数,真是神器。在java中如何实现呢?

先是搜一搜。奇怪的是,关于networkdays的其他语言实现,竟然没有一搜就一大把的结果,难道这个函数的使用很小众?

stackoverflow上,有同学推荐objectlabkit这个库,并且用此库实现了networkdays。然而下载objectlabkit后,发现其实现方案竟然是从开始日期一天一天的向截止日期推进(希望是我看错了……)

 

如标题中的“简化版”,也为突出重点,本文的关注点将是对networkdays(start_date, end_date)进行算法分析和java实现,对networkdays的第三个参数,将不考虑。

算法步骤分析:

1、首先求出截止日期end与开始日期start间的天数days;

2、获取开始日期start的周次号startWeekno,截止日期end的周次号endWeekno;

3、将开始日期start加上(endWeekno-startWeekno),即将产生一个新的开始日期newStart,newStart的周次号与截止日期end的周次号相同(这个是显而易见的)。由前,在对两个端点的周次号进行统一化后,即是使用end-(start+(endWeekno-startWeekno))=>days-(endWeekno-startWeekno)的方案,得出一个临时的天数tmpDays。

3.1 这一段说明是为了解释:对tmpDays来说,其值有可能与days相同,也有可能大于days,也有可能小于days,无论哪种,我们之后再使用加(endWeekno-startWeekno)进行补偿,使其与days相同;但到现在,我们能够得知tmpDays是可以被7整除的;

4、由上步,首先能得到一个工作日天数:tmpWorkdaysA=tmpDays/7*5;

5、如3.1中说明的,我们还需对tmpWorkdaysA进行补偿:tmpWorkdaysB=tmpWorkdaysA+(endWeekno-startWeekno);

6、tmpWorkdaysC=tmpWorkdaysB+1。加1的原因是对start=2018-03-07、end=2018-03-08来说,其days为1,然而是两个工作日,这也是显而易见的:数数的时候显然是end-start+1;

7、以上的步骤仅能保证start不为周日、end不为周六(第6步加1就将两个端点日期都进行了包含)。因此当start为周日(starWeekno默认为1)时,需要将结果tmpWorkdaysC减1;当end为周六时,需要将结果tmpWorkdaysC也减1。

8、至此,计算结束。

 

由以上,可得如下的java代码:

 

package com.bn.zbase.finance;

import java.util.Calendar;
import java.util.Date;

/**
 * Created by zcn on 2018/3/7.
 */
public class NetworkdaysTest {

    public static void main(String[] args) throws Exception {
        Calendar objStart = Calendar.getInstance();
        Calendar objEnd = Calendar.getInstance();

        objStart.set(2018, 2 - 1, 23);
        objEnd.set(2018, 3 - 1, 3);
        System.out.println("[2018-02-23, 2018-03-03] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));

        objStart.set(2018, 2 - 1, 24);
        objEnd.set(2018, 3 - 1, 3);
        System.out.println("[2018-02-24, 2018-03-03] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));

        objStart.set(2018, 2 - 1, 25);
        objEnd.set(2018, 3 - 1, 3);
        System.out.println("[2018-02-25, 2018-03-03] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));

        objStart.set(2018, 2 - 1, 23);
        objEnd.set(2018, 3 - 1, 2);
        System.out.println("[2018-02-23, 2018-03-02] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));

        objStart.set(2018, 2 - 1, 22);
        objEnd.set(2018, 3 - 1, 6);
        System.out.println("[2018-02-22, 2018-03-06] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));

        objStart.set(2018, 3 - 1, 2);
        objEnd.set(2018, 3 - 1, 3);
        System.out.println("[2018-03-02, 2018-03-03] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));

        objStart.set(2018, 3 - 1, 3);
        objEnd.set(2018, 3 - 1, 3);
        System.out.println("[2018-03-03, 2018-03-03] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));

        objStart.set(2018, 3 - 1, 4);
        objEnd.set(2018, 3 - 1, 4);
        System.out.println("[2018-03-04, 2018-03-04] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));

        objStart.set(2018, 3 - 1, 3);
        objEnd.set(2018, 3 - 1, 4);
        System.out.println("[2018-03-03, 2018-03-04] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));

        objStart.set(2018, 3 - 1, 4);
        objEnd.set(2018, 3 - 1, 10);
        System.out.println("[2018-03-04, 2018-03-10] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));

        objStart.set(1970, 1 - 1, 1);
        objEnd.set(2018, 3 - 1, 2);
        System.out.println("[1970-01-01, 2018-03-02] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));

        objStart.set(1970, 1 - 1, 1);
        objEnd.set(2018, 3 - 1, 3);
        System.out.println("[1970-01-01, 2018-03-03] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));

        objStart.set(1970, 1 - 1, 1);
        objEnd.set(2018, 3 -1, 4);
        System.out.println("[1970-01-01, 2018-03-04] workdays : " + networkDays(objStart.getTime(), objEnd.getTime()));
    }

    /**
     * 求两个日期之间的工作日天数。
     * @param start
     * @param end
     * @return
     */
    public static long networkDays(Date start, Date end) throws Exception {
        long iRet = -1;

        if (start == null || end == null)
            return iRet;

        start = adjustDateTime2Zero(start);
        end = adjustDateTime2Zero(end);
        if (start.after(end))
            return iRet;

        Calendar objStart = Calendar.getInstance();
        Calendar objEnd = Calendar.getInstance();
        objStart.setTime(start);
        objEnd.setTime(end);

        int iStartWeekno = objStart.get(Calendar.DAY_OF_WEEK);
        int iEndWeekno = objEnd.get(Calendar.DAY_OF_WEEK);
        long days = (objEnd.getTimeInMillis() - objStart.getTimeInMillis()) / (1000 * 60 * 60 * 24);
        long daysWithoutWeekendDays = (days - (iEndWeekno - iStartWeekno)) / 7 * 5;

        iRet = daysWithoutWeekendDays + (iEndWeekno - iStartWeekno) + 1;
        iRet = iEndWeekno == Calendar.SATURDAY ? iRet - 1 : iRet;
        iRet = iStartWeekno == Calendar.SUNDAY ? iRet - 1 : iRet;

        return iRet;
    }

    /**
     * 将日期对象的时间部分调整为00:00:00.000
     * @param objDate
     * @return
     */
    private static Date adjustDateTime2Zero(Date objDate) throws Exception {
        if (objDate == null)
            throw new Exception("输入日期对象为null");

        Calendar objAjust = Calendar.getInstance();
        objAjust.setTime(objDate);
        objAjust.set(Calendar.HOUR_OF_DAY, 0);
        objAjust.set(Calendar.MINUTE, 0);
        objAjust.set(Calendar.SECOND, 0);
        objAjust.set(Calendar.MILLISECOND, 0);
        return objAjust.getTime();
    }
}

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章