excel函数networkdays在java中的实现[简化版]
由于某个业务需要计算两个日期间的除去工作日、法定假日的工作日天数,然后想起来excel里面有个networkdays函数,真是神器。在java中如何实现呢?
先是搜一搜。奇怪的是,关于networkdays的其他语言实现,竟然没有一搜就一大把的结果,难道这个函数的使用很小众?
*上,有同学推荐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();
}
}
上一篇: 曹操不称帝曹丕却迫不及待地要当皇帝?两父子为何选择如此不同?
下一篇: 数据结构操作模版