由於某個業務需要計算兩個日期間的除去工作日、法定假日的工作日天數,然後想起來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();
}
}