Java 计算两个日期之间的工作日数
我需要计算两个给定日期之间的工作日数Java 计算两个日期之间的工作日数,java,date,Java,Date,我需要计算两个给定日期之间的工作日数 我将假期列表作为用户提供的数组列表。 因此,我可以调查日期之间的每一天,并检查其是否为工作日,而不是像我下面提供的代码那样的联邦假日(工作正常) 但这是非常昂贵的,比如说12个联邦假日,每天我都要检查,而不是周末, 因此,如果我需要计算5年,它将需要365*5*12及其21000次迭代!这太疯狂了(甚至不包括工作日的计算) 有更好的办法吗 package test; import java.text.DateFormat; import java.text
我将假期列表作为用户提供的数组列表。
因此,我可以调查日期之间的每一天,并检查其是否为工作日,而不是像我下面提供的代码那样的联邦假日(工作正常) 但这是非常昂贵的,比如说12个联邦假日,每天我都要检查,而不是周末,
因此,如果我需要计算5年,它将需要365*5*12及其21000次迭代!这太疯狂了(甚至不包括工作日的计算)
有更好的办法吗
package test;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.time.DateUtils;
public class TestDiff {
public static void main(String[] args) throws ParseException {
DateFormat formatter = new SimpleDateFormat("MM/dd/yy");
// add 4 years as an example
Date fromDate = formatter.parse("11/06/2017"),toDate = formatter.parse("11/29/2017");// DateUtils.addDays(fromDate,365 * 4);
int numberOfDaysCount=0;
int daysBetween = daysBetween(fromDate,toDate);
Date caurDate = fromDate;
for(int i=0;i<=daysBetween ; i++ ) {
if(isWeekDay(caurDate) && !isFederalHoliday(caurDate) )
numberOfDaysCount++;
caurDate = DateUtils.addDays(caurDate,1); // add one day
}
System.out.println("number of business days between "+fromDate+" and "+toDate+" is: "+numberOfDaysCount);
}
private static boolean isWeekDay(Date caurDate) {
Calendar c = Calendar.getInstance();
c.setTime(caurDate);
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
return dayOfWeek!= Calendar.SATURDAY && dayOfWeek!= Calendar.SUNDAY ;
}
private static boolean isFederalHoliday(Date caurDate) throws ParseException {
DateFormat formatter = new SimpleDateFormat("MM/dd/yy"); //list will come from dao.getFederalHoliday();
List<Date> federalHolidays = Arrays.asList(formatter.parse("01/02/2017"),formatter.parse("01/16/2017"),formatter.parse("02/20/2017"),formatter.parse("05/29/2017"),formatter.parse("07/04/2017"),formatter.parse("09/04/2017"),formatter.parse("10/09/2017"),formatter.parse("07/04/2017"),formatter.parse("11/10/2017"),formatter.parse("11/23/2017"),formatter.parse("12/25/2017"));
for (Date holiday : federalHolidays) {
if(DateUtils.isSameDay(caurDate,holiday)) //using Apache commons-lang
return true;
}
return false;
}
public static int daysBetween(Date d1, Date d2){
return (int)( (d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24));
}
}
封装测试;
导入java.text.DateFormat;
导入java.text.ParseException;
导入java.text.simpleDataFormat;
导入java.util.array;
导入java.util.Calendar;
导入java.util.Date;
导入java.util.List;
导入org.apache.commons.lang3.time.DateUtils;
公共类TestDiff{
公共静态void main(字符串[]args)引发异常{
DateFormat格式化程序=新的SimpleDateFormat(“MM/dd/yy”);
//以4年为例
日期fromDate=formatter.parse(“2017年6月11日”),toDate=formatter.parse(“2017年11月29日”);//DateUtils.addDays(fromDate,365*4);
int numberofdaysunt=0;
int daysBetween=daysBetween(从日期到日期);
日期尾日期=起始日期;
对于(int i=0;i,注释中已经给出了许多示例,用于计算两个日期之间的工作日数
就减去联邦假日而言。与其在fromdate-todate范围内的所有日期上循环,不如在fromdate-todate范围内每年循环一次federalHoliday数组中的所有条目
请原谅伪代码:
int workdays = getWeekdayCount();
for(int i = 0, count = getYearsBetween(); i < count; ++i)
{
startIndex = (i==0?getIndexFirstHoliday():0);
endIndex = (i==(count-1)?getIndexLastHoliday():11);
for(; startIndex <= endIndex; ++startIndex)
{
if(!federalHolidays[startIndex].IsWeekday(count))
workdays--;
}
}
int workdays=getWeekdayCount();
for(int i=0,count=getYearsBetween();i 对于(;startIndex,这里有一个答案是使用Java.time.*
在Java8中实现的
public class TestSo47314277 {
/**
* A set of federal holidays. Compared to iteration, using a
* hash-based container provides a faster access for reading
* element via hash code. Using {@link Set} avoids duplicates.
* <p>
* Add more dates if needed.
*/
private static final Set<LocalDate> HOLIDAYS;
static {
List<LocalDate> dates = Arrays.asList(
LocalDate.of(2017, 1, 2),
LocalDate.of(2017, 1, 16),
LocalDate.of(2017, 2, 20),
LocalDate.of(2017, 5, 29),
LocalDate.of(2017, 7, 4),
LocalDate.of(2017, 9, 4),
LocalDate.of(2017, 10, 9),
LocalDate.of(2017, 11, 10),
LocalDate.of(2017, 11, 23),
LocalDate.of(2017, 12, 25)
);
HOLIDAYS = Collections.unmodifiableSet(new HashSet<>(dates));
}
public int getBusinessDays(LocalDate startInclusive, LocalDate endExclusive) {
if (startInclusive.isAfter(endExclusive)) {
String msg = "Start date " + startInclusive
+ " must be earlier than end date " + endExclusive;
throw new IllegalArgumentException(msg);
}
int businessDays = 0;
LocalDate d = startInclusive;
while (d.isBefore(endExclusive)) {
DayOfWeek dw = d.getDayOfWeek();
if (!HOLIDAYS.contains(d)
&& dw != DayOfWeek.SATURDAY
&& dw != DayOfWeek.SUNDAY) {
businessDays++;
}
d = d.plusDays(1);
}
return businessDays;
}
}
公共类测试SO47314277{
/**
*一组联邦假日。与迭代相比,使用
*基于散列的容器提供了更快的读取访问
*元素。使用{@link Set}可避免重复。
*
*如果需要,添加更多日期。
*/
私人假期;
静止的{
列表日期=Arrays.asList(
本地日期(2017年1月2日),
本地日期(2017年1月16日),
本地日期(2017年2月20日),
本地日期(2017年5月29日),
本地日期(2017年7月4日),
本地日期(2017年9月4日),
本地日期(2017年10月9日),
本地日期(2017年11月10日),
本地日期(2017年11月23日),
本地日期(2017年12月25日)
);
假日=集合。不可修改集(新哈希集(日期));
}
public int getBusinessDays(LocalDate startInclusive,LocalDate endExclusive){
if(起始结论性。isAfter(endExclusive)){
字符串msg=“开始日期”+开始结论
+“必须早于结束日期”+endExclusive;
抛出新的IllegalArgumentException(msg);
}
int businessDays=0;
LocalDate d=startInclusive;
while(d.isBefore(endExclusive)){
DayOfWeek dw=d.getDayOfWeek();
如果(!HOLIDAYS.contains(d)
&&dw!=星期六
&&dw!=星期天(星期日){
商业日报++;
}
d=d.plusDays(1);
}
返回工作日;
}
}
blow代码是我用java 8编写的完整项目,将解决您的问题
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class App {
public static void main(String[] args) {
System.out.println("Hello World!");
LocalDate today = LocalDate.of(2020, 5, 5);
// Add one holiday for testing
List<LocalDate> holidays = new ArrayList<>();
holidays.add(LocalDate.of(2020, 5, 11));
holidays.add(LocalDate.of(2020, 5, 1));
for (LocalDate x : BusinessDaysBetween(today, today.plusDays(20), Optional.empty()))
{
System.out.println(x.toString());
}
}
private static List<LocalDate> BusinessDaysBetween(LocalDate startDate, LocalDate endDate,
Optional<List<LocalDate>> holidays)
{
if (startDate == null || endDate == null || !holidays.isPresent()) {
throw new IllegalArgumentException("Invalid method argument(s) to countBusinessDaysBetween(" + startDate
+ "," + endDate + "," + holidays + ")");
}
Predicate<LocalDate> isHoliday = date -> holidays.map(localDates -> localDates.contains(date)).orElse(false);
Predicate<LocalDate> isWeekend = date -> date.getDayOfWeek() == DayOfWeek.SATURDAY
|| date.getDayOfWeek() == DayOfWeek.SUNDAY;
long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);
return Stream.iterate(startDate, date -> date.plusDays(1)).limit(daysBetween)
.filter(isHoliday.or(isWeekend).negate()).collect(Collectors.toList());
}
}
import java.time.DayOfWeek;
导入java.time.LocalDate;
导入java.time.temporal.ChronoUnit;
导入java.util.ArrayList;
导入java.util.List;
导入java.util.Optional;
导入java.util.function.Predicate;
导入java.util.stream.collector;
导入java.util.stream.stream;
公共类应用程序{
公共静态void main(字符串[]args){
System.out.println(“你好,世界!”);
LocalDate today=LocalDate.of(2020,5,5);
//添加一个假期进行测试
列表假日=新建ArrayList();
添加(LocalDate.of(2020,5,11));
添加(LocalDate.of(2020,5,1));
for(LocalDate x:BusinessDaysBetween(今天,今天.plusDays(20),可选.empty())
{
System.out.println(x.toString());
}
}
私有静态列表BusinessDaysBetween(LocalDate startDate、LocalDate endDate、,
(可选假日)
{
if(startDate==null | | endDate==null | |!holidays.isPresent()){
向countBusinessDaysBetween(“+startDate”)抛出新的IllegalArgumentException(“无效的方法参数
+“,“+截止日期+”,“+节假日+”);
}
谓词isHoliday=date->holidays.map(localDates->localDates.contains(date)).orElse(false);
谓词为weekend=date->date.getDayOfWeek()==DayOfWeek.SATURDAY
||date.getDayOfWeek()=DayOfWeek.SUNDAY;
long daysBetween=计时单位天数介于(开始日期、结束日期)之间;
返回流.iterate(startDate,date->date.plusDays(1)).limit(daysBetween)
.filter(isHoliday.or(isWeekend.negate()).collect(Collectors.toList());
}
}
您可以执行以下操作,而不是反复检查每一天,检查它是否是一周而不是假日:
- 获取两次约会之间的天数
- 计算完整周数,计算潜在工作日数(乘以5)或要减去的周末数(乘以2)
- 计算为部分周调整的周天数
- 获取所需范围内的非周末假期数,然后减去这些假期数(对于
n
假期,每年最多只能进行n
检查,甚至可以进行优化)
//start is inclusive, end is exclusive
public long getBusinessDays(LocalDate start, LocalDate end) {
long days = ChronoUnit.DAYS.between(start, end);
long fullWeeks = days/7;
//day of week value ranges from 1 (Monday) to 7 (Sunday)
//clamp Sunday to Saturday (so treat them as one day) - range is now 1 to 6
//business days is the difference in days if dow(start) < dow(end)
//if start and end are on a weekend we'll get 6-6 = 0
long partialWeekAdjustment = Math.min(6, end.getDayOfWeek().getValue()) - Math.min(6, start.getDayOfWeek().getValue() );
//if the result is negative, we have dow(start) > dow(end) so add 5 business days
//ex.: thu (4) to wed (3) will be 3-4 = -1, so adding 5 will result in 4 (thu, fri, mon, tue)
if( partialWeekAdjustment < 0 ) {
partialWeekAdjustment += 5;
}
//get the number of non-weekend holidays between the 2 dates
long numNonWeekendHolidays = getNonWeekendHolidays(start, end);
long businessDays = fullWeeks * 5 + partialWeekAdjustment - numNonWeekendHolidays;
return businessDays;
}
private long getNonWeekendHolidays(LocalDate start, LocalDate end) {
//get this from somewhere, could also be something else
SortedSet<LocalDate> holidays = ...;
//get holidays between the 2 dates, filter for non-weekend and count
return holidays.subSet(start, end).stream()
.filter(d -> d.getDayOfWeek().compareTo(DayOfWeek.SATURDAY) < 0)
.count();
//if weekend and non-weekend holidays are kept separate this could be:
// SortedSet<LocalDate> nonWeekendHolidays = ...;
// return nonWeekendHolidays.subSet(start, end).size();
}