我要求计算两个给定日期之间的营业日数 我将假期列表作为用户提供的数组列表 所以我可以调查日期之间的每一天,检查它的工作日而不是联邦假期,就像我在下面提供的代码一样(工作正常)
但这是非常昂贵的,比如12个联邦假期,每天我都要检查它不是周末,
因此,如果我需要计算5年之间的数量,它需要365 * 5 * 12的21,000次迭代!它的疯狂(甚至不包括工作日的计算)
还有更好的方法吗?
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));
}
}
答案 0 :(得分:3)
以下是使用java.time.*
在Java 8中实现的答案。
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;
}
}
答案 1 :(得分:1)
评论中已经提供了很多例子来计算这两个日期之间的工作日数。
就减去联邦假期而言。为什么不在你的fromDate-toDate范围内每年对你的fedeHoliday数组中的所有条目进行循环,而不是遍历fromdate-todate范围内的所有日期。
请原谅伪代码:
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--;
}
}
这样,您每年最多循环12次,再加上另外2 * 12来获取第一个和最后一个索引。
答案 2 :(得分:1)
您可以执行以下操作,而不是遍历每一天并检查它是否是工作日而不是假期:
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();
}
答案 3 :(得分:0)
打击代码是我用Java 8编写的完整项目,可以解决您的问题。
std::unordered_set<IntSet>