計算工作日天數-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();
    }
}

 

 

 

 

 

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