计算Java中两个日期之间的工作日数

时间:2011-01-05 01:14:33

标签: java

任何人都可以向我指出一些Java片段,其中我可以在两个日期之间获得业务(周六和周日除外)。

20 个答案:

答案 0 :(得分:47)

解决方案无循环

static long days(Date start, Date end){
    //Ignore argument check

    Calendar c1 = Calendar.getInstance();
    c1.setTime(start);
    int w1 = c1.get(Calendar.DAY_OF_WEEK);
    c1.add(Calendar.DAY_OF_WEEK, -w1);

    Calendar c2 = Calendar.getInstance();
    c2.setTime(end);
    int w2 = c2.get(Calendar.DAY_OF_WEEK);
    c2.add(Calendar.DAY_OF_WEEK, -w2);

    //end Saturday to start Saturday 
    long days = (c2.getTimeInMillis()-c1.getTimeInMillis())/(1000*60*60*24);
    long daysWithoutWeekendDays = days-(days*2/7);

    // Adjust days to add on (w2) and days to subtract (w1) so that Saturday
    // and Sunday are not included
    if (w1 == Calendar.SUNDAY && w2 != Calendar.SATURDAY) {
        w1 = Calendar.MONDAY;
    } else if (w1 == Calendar.SATURDAY && w2 != Calendar.SUNDAY) {
        w1 = Calendar.FRIDAY;
    } 

    if (w2 == Calendar.SUNDAY) {
        w2 = Calendar.MONDAY;
    } else if (w2 == Calendar.SATURDAY) {
        w2 = Calendar.FRIDAY;
    }

    return daysWithoutWeekendDays-w1+w2;
}

答案 1 :(得分:42)

public static int getWorkingDaysBetweenTwoDates(Date startDate, Date endDate) {
    Calendar startCal = Calendar.getInstance();
    startCal.setTime(startDate);        

    Calendar endCal = Calendar.getInstance();
    endCal.setTime(endDate);

    int workDays = 0;

    //Return 0 if start and end are the same
    if (startCal.getTimeInMillis() == endCal.getTimeInMillis()) {
        return 0;
    }

    if (startCal.getTimeInMillis() > endCal.getTimeInMillis()) {
        startCal.setTime(endDate);
        endCal.setTime(startDate);
    }

    do {
       //excluding start date
        startCal.add(Calendar.DAY_OF_MONTH, 1);
        if (startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY && startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
            ++workDays;
        }
    } while (startCal.getTimeInMillis() < endCal.getTimeInMillis()); //excluding end date

    return workDays;
}
  

开始日期和结束日期是独家的,只有给定的日期   日期将被计算在内。不包括开始日期和结束日期。

答案 2 :(得分:12)

5行代码中没有循环的解决方案

天数与ChronoUnit.DAYS.between(start, end)的定义方式相同,这意味着星期一到星期五之间有4天。由于我们只对工作日感兴趣,因此我们必须减去周末,因此从周五到周二将有2个工作日(仅计算endDay - startDay并减去周末的2)。如果您想要包含结果,请在结果中添加1,即不是天数。

我提出两种解决方案。

第一种解决方案(5线,短而神秘):

import java.time.*;
import java.time.temporal.*;

public static long calcWeekDays1(final LocalDate start, final LocalDate end) {
    final DayOfWeek startW = start.getDayOfWeek();
    final DayOfWeek endW = end.getDayOfWeek();

    final long days = ChronoUnit.DAYS.between(start, end);
    final long daysWithoutWeekends = days - 2 * ((days + startW.getValue())/7);

    //adjust for starting and ending on a Sunday:
    return daysWithoutWeekends + (startW == DayOfWeek.SUNDAY ? 1 : 0) + (endW == DayOfWeek.SUNDAY ? 1 : 0);
}

第二个解决方案:

public static long calcWeekDays2(final LocalDate start, final LocalDate end) {
    final int startW = start.getDayOfWeek().getValue();
    final int endW = end.getDayOfWeek().getValue();

    final long days = ChronoUnit.DAYS.between(start, end);
    long result = days - 2*(days/7); //remove weekends

    if (days % 7 != 0) { //deal with the rest days
        if (startW == 7) {
            result -= 1;
        } else if (endW == 7) {  //they can't both be Sunday, otherwise rest would be zero
            result -= 1;
        } else if (endW < startW) { //another weekend is included
            result -= 2;
        }
    }

    return result;
}

答案 3 :(得分:10)

我使用了盛源路的解决方案,但我需要修正一个方法被调用的情况,其中一个日期是在星期六而另一个是星期日 - 否则答案是关闭一天:

static long days(Date start, Date end){
    //Ignore argument check

    Calendar c1 = GregorianCalendar.getInstance();
    c1.setTime(start);
    int w1 = c1.get(Calendar.DAY_OF_WEEK);
    c1.add(Calendar.DAY_OF_WEEK, -w1 + 1);

    Calendar c2 = GregorianCalendar.getInstance();
    c2.setTime(end);
    int w2 = c2.get(Calendar.DAY_OF_WEEK);
    c2.add(Calendar.DAY_OF_WEEK, -w2 + 1);

    //end Saturday to start Saturday 
    long days = (c2.getTimeInMillis()-c1.getTimeInMillis())/(1000*60*60*24);
    long daysWithoutSunday = days-(days*2/7);

    if (w1 == Calendar.SUNDAY) {
        w1 = Calendar.MONDAY;
    }
    if (w2 == Calendar.SUNDAY) {
        w2 = Calendar.MONDAY;
    }
    return daysWithoutSunday-w1+w2;
}

答案 4 :(得分:5)

java.time

现代方法是使用java.time类。

LocalDate

LocalDate类表示没有时间且没有时区的仅限日期的值。

LocalDate start = LocalDate.of( 2016 , 1 , 23 );
LocalDate stop = start.plusMonths( 1 );

DayOfWeek枚举

DayOfWeek enum为一周中的每个服务器日提供singleton个实例。

DayOfWeek dow = start.getDayOfWeek();
if( dow.equals( DayOfWeek.SATURDAY ) || dow.equals( DayOfWeek.SUNDAY ) ) …

我们可以收集所需日期为List

int initialCapacity = Duration.between( start , stop ).toDays() ;
List<LocalDate> dates = new ArrayList<>( initialCapacity );
…
if( dow.equals( DayOfWeek.SATURDAY ) || dow.equals( DayOfWeek.SUNDAY ) ) {
    dates.add( date );
    …

EnumSet是一种非常高效,快速且内存不足的Set实现。我们可以使用EnumSet代替上面提到的if语句。

Set<DayOfWeek> weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY ) ;
…
if( weekend.contains( dayOfWeek ) ) …

把它们放在一起。

LocalDate date = start ;
while( date.isBefore( stop ) ) {
    if( ! weekend.contains( date.getDayOfWeek() ) ) { // If not weekend, collect this LocalDate.
        dates.add( date ) ;
    }
    // Prepare for next loop.
    date = date.plusDays( 1 ); // Increment to next day.
}

nextWorkingDay TemporalAdjuster

另一种方法是使用ThreeTen-Extra项目添加适用于java.time的类。

Temporals类添加了TemporalAdjuster的其他实现来操作日期时间值。我们希望nextWorkingDay调整器在跳过星期六和星期六时增加日期。周日。

LocalDate start = LocalDate.of( 2016 , 1 , 23 );
LocalDate stop = start.plusMonths( 1 );

int initialCapacity = Duration.between( start , stop ).toDays() ;
List<LocalDate> dates = new ArrayList<>( initialCapacity );

LocalDate date = start.minusDays( 1 );  // Start a day ahead.
while( date.isBefore( stop ) ) {
    date = date.with( org.threeten.extra.Temporals.nextWorkingDay() );
    // Double-check ending date as the `nextWorkingDay` adjuster could move us past the stop date.
    if( date.isBefore( stop ) ) { 
        dates.add( date ) ;
    }
}

性能

我对本页各种答案中各种方法的表现感到好奇。我只考虑现代的 java.time 代码,而不是使用麻烦的遗留Date / Calendar类的代码。

以下是四种方法,每种方法都会返回经过的天数。

一个人使用Answer by Roland中看到的基于数学的聪明方法。

private long countWeekDaysMath ( LocalDate start , LocalDate stop ) {
    // Code taken from Answer by Roland.
    // https://stackoverflow.com/a/44942039/642706
    long count = 0;
    final DayOfWeek startW = start.getDayOfWeek();
    final DayOfWeek stopW = stop.getDayOfWeek();

    final long days = ChronoUnit.DAYS.between( start , stop );
    final long daysWithoutWeekends = days - 2 * ( ( days + startW.getValue() ) / 7 );

    //adjust for starting and ending on a Sunday:
    count = daysWithoutWeekends + ( startW == DayOfWeek.SUNDAY ? 1 : 0 ) + ( stopW == DayOfWeek.SUNDAY ? 1 : 0 );

    return count;
}

本答案中的两种使用方法:(a)访问每个日期,在传统循环中逐个递增。

private long countWeekDaysVisit ( LocalDate start , LocalDate stop ) {
    // Code taken from Answer by Basil Bourque.
    // https://stackoverflow.com/a/40369140/642706
    long count = 0;
    Set < DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
    LocalDate ld = start;
    while ( ld.isBefore( stop ) ) {
        if ( ! weekend.contains( ld.getDayOfWeek() ) ) { // If not weekend, collect this LocalDate.
            count++;
        }
        // Prepare for next loop.
        ld = ld.plusDays( 1 ); // Increment to next day.
    }
    return count;
}

......和,(b)使用TemporalAdjuster实施org.threeten.extra.Temporals.nextWorkingDay()

private long countWeekDaysAdjuster ( LocalDate start , LocalDate stop ) {
    // Code taken from Answer by Basil Bourque.
    // https://stackoverflow.com/a/40369140/642706
    long count = 0;
    Set < DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
    TemporalAdjuster nextWorkingDayTA = org.threeten.extra.Temporals.nextWorkingDay();
    LocalDate ld = start;
    if ( weekend.contains( ld.getDayOfWeek() ) ) {
        ld = ld.with( nextWorkingDayTA );
    }
    while ( ld.isBefore( stop ) ) {
        count++;
        // Prepare for next loop.
        ld = ld.with( nextWorkingDayTA ); // Increment to next working day (non-weekend day).
    }
    return count;
}

最后一个使用了Answer by Ravindra Ranwala中的Java Streams方法。

private long countWeekDaysStream ( LocalDate start , LocalDate stop ) {
    // Code taken from the Answer by Ravindra Ranwala.
    // https://stackoverflow.com/a/51010738/642706
    long count = 0;
    Set < DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
    final long weekDaysBetween = start.datesUntil( stop )
                                     .filter( d -> ! weekend.contains( d.getDayOfWeek() ) )
                                     .count();
    return count;
}

测试安全带。

警告:

  • 嗯,关于微观基准测试的常见警告是不值得信任的,容易产生不合理或不切实际的结论。
  • 我希望我学会使用JMH micro-benchmarking framework
  • 我没有打算尝试优化任何此代码。例如,在实际工作中,TemporalAdjuster可以在我们的方法之外缓存。

测试工具。

LocalDate start = LocalDate.of( 2018 , Month.JANUARY , 1 );
LocalDate stop = start.plusYears( 1 );

int runs = 100_000;

long go = System.nanoTime();
for ( int i = 1 ; i <= runs ; i++ ) {
    long count = this.countWeekDaysMath( start , stop );
}
long elapsedMath = ( System.nanoTime() - go );

go = System.nanoTime();
for ( int i = 1 ; i <= runs ; i++ ) {
    long count = this.countWeekDaysVisit( start , stop );
}
long elapsedVisit = ( System.nanoTime() - go );

go = System.nanoTime();
for ( int i = 1 ; i <= runs ; i++ ) {
    long count = this.countWeekDaysStream( start , stop );
}
long elapsedAdjuster = ( System.nanoTime() - go );

go = System.nanoTime();
for ( int i = 1 ; i <= runs ; i++ ) {
    long count = this.countWeekDaysStream( start , stop );
}
long elapsedStream = ( System.nanoTime() - go );

System.out.println( "math: " + elapsedMath + " each: " + ( elapsedMath / runs ) );
System.out.println( "visit: " + elapsedVisit + " each: " + ( elapsedVisit / runs ) );
System.out.println( "adjuster: " + elapsedAdjuster + " each: " + ( elapsedAdjuster / runs ) );
System.out.println( "stream: " + elapsedStream + " each: " + ( elapsedStream / runs ) );

使用Oracle JDK 10.0.1和 ThreeTen-Extra 版本1.3.2在我的MacBook Pro(Sierra)上运行时,我得到的结果始终接近以下内容。正如我们所期望的那样,数学解决方案只是其他一小部分,几百纳米,而不是几千纳米。在其他三个中, TemporalAdjuster是最长的,每个总是超过10,000纳米。访问量和流量均低于10,000纳米,访问量明显快于流量。正如围绕互联网的其他示例所示, Java Streams通常会生成漂亮的短代码,而且通常会运行得更长,在这种情况下会长约20%。

  

数学:18313309每个:183

     

访问:708420626每个:7084

     

理算师:1002157240每人:10021

     

stream:924724750 each:9247

关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和&amp; SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore

答案 5 :(得分:4)

我没有基于Java的解决方案,但有一个PHP,希望它有所帮助:

function getDate($days) {   
    for ($i = 0; $i < $days; $i ++) {                                      
        if (date('N' , strtotime('+' . ($i + 1) . ' days')) > 5) {  
            $days++;                                                        
        }                                                                   
    }                                                                       

    return date('l, F jS', strtotime('+' . $days . ' days', time()));
}

答案 6 :(得分:3)

几乎所有解决方案都已过时且难以理解。但是,这是一个非常简洁易读的解决方案。

此方法使用Java 9及更高版本中内置的Java Stream方法提供的LocalDate::datesUntil

you split the **str** into substrings and stored in **res** array,again 
your 
trying to split the substrings, you should specify that which substring going 
to split. 
void displayResult() {
String str = "tamilnadu||chennai-karanataka||bengaluru";
String[] res = str.split("\\-");
System.out.println(res.length);
**String[] result = res[0].toString().split("\\|");**
for (String string : result) {
System.out.println(string);
}
System.out.println(result.length);
}
output:
2 
tmailnadu
chennai
3
  

LocalDate startDate = LocalDate.of(2018, 5, 2); LocalDate endDate = LocalDate.now(); Set<DayOfWeek> weekend = EnumSet.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY); final long weekDaysBetween = startDate.datesUntil(endDate) .filter(d -> !weekend.contains(d.getDayOfWeek())) .count(); 返回按顺序排列的日期流。的   返回的流从该日期(包括该日期)开始,然后转到   endExclusive(独占)(以1天为增量)。

然后将所有星期六和星期日过滤掉。最后一步是获取剩余工作日的计数。

Java-9已于一年前发布,因为现在对我来说使用它是合理的。

答案 7 :(得分:2)

这是我没有循环的例子。算法与卢声远 Shengyuan Lus一样,但我使用了JodaTime的一些功能。

{{1}}

答案 8 :(得分:2)

Piyush解决方案中的do while错误,应该是:

do {
    if (startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY && startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
        ++workDays;
    }
    startCal.add(Calendar.DAY_OF_MONTH, 1);
} while (startCal.getTimeInMillis() < endCal.getTimeInMillis());

答案 9 :(得分:1)

这是我没有循环的例子。它是此示例中的一个类,因为我在一些JSON输出中对其进行序列化。基本上我计算出两个日期之间的天数,除以7并指定一个长整数值来表示周数。取原始天数并减去周末数* 2。这不是很完美 - 如果有一个“宿醉”,你需要解决这个问题。开始接近一周结束并持续周末。为了纠正这个问题,我会在开始时找到一周中的某一天并找到剩余的天数,然后将它们加在一起以找到“宿醉”。 - 如果超过5则是周末。它并不完美,根本不考虑假期。看见没有Joda。也就是说时区也存在问题。

import java.io.Serializable;
import java.util.Date;

public class BusinessDayCalculator implements Serializable {

    private static long DAY = 86400000l;

    private Date startTime;
    private Date endTime;

    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }

    public Date getStartTime() {
        return startTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

    public Date getEndTime() {
        return endTime;
    }

    public long getHours() {
        return (this.endTime.getTime() - this.startTime.getTime())/(1000*60*60);
    }

    public long getBusinessDays(){

        long startDay = getDayFromDate(this.startTime);
        long endDay = getDayFromDate(this.endTime);

        long totalDays = endDay-startDay;
        long totalWeekends = totalDays/7;

        long day = getDay(this.startTime);

        long hangover = totalDays % 7;

        long intoWeekend = day + hangover;
        if(intoWeekend>5){
            totalWeekends++;
        }

        long totalBusinessDays = totalDays - (totalWeekends *2);

        /*
        System.out.println("Days = " + day );
        System.out.println("Hangover = " + hangover );
        System.out.println("Total Days = " + totalDays);
        System.out.println("Total Weekends = " + totalWeekends);
        System.out.println("Total Business Days = " + totalBusinessDays);
        */

        return totalBusinessDays;
    }

    private long getDayFromDate( Date date ){
        long d = date.getTime() / DAY;
        return d;
    }

    private long getDay( Date date ){
        long daysSinceEpoc = getDayFromDate(date);
        long day = daysSinceEpoc % 7;
        day = day + 4;
        if(day>6) day = day - 7;
        return day;
    }

}

答案 10 :(得分:1)

startCal.add应该添加到Calendar.DATE字段,而不是Calendar.DAY_OF_MONTH,我在12月/ 1月期间得到了奇怪的结果。

答案 11 :(得分:1)

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * 
 * @author varun.vishwakarma
 *
 */
public class FindWeekendsInDateRange {
	static HashMap<Integer, String> daysOfWeek=null;
	
	
	static {
		daysOfWeek = new HashMap<Integer, String>();
		daysOfWeek.put(new Integer(1), "Sun");
		daysOfWeek.put(new Integer(2), "Mon");
		daysOfWeek.put(new Integer(3), "Tue");
		daysOfWeek.put(new Integer(4), "Wed");
		daysOfWeek.put(new Integer(5), "Thu");
		daysOfWeek.put(new Integer(6), "Fri");
		daysOfWeek.put(new Integer(7), "Sat");
	}

	/**
	 * 
	 * @param from_date 
	 * @param to_date
	 * @return 
	 */
	public static List<Date>  calculateWeekendsInDateReange(Date fromDate, Date toDate) {
		List<Date> listOfWeekends = new ArrayList<Date>();
			Calendar from = Calendar.getInstance();
			Calendar to = Calendar.getInstance();
			from.setTime(fromDate);
			to.setTime(toDate);
			while (from.getTimeInMillis() < to.getTimeInMillis()) {
				if (daysOfWeek.get(from.get(Calendar.DAY_OF_WEEK)) == "Sat") {
					Date sat = from.getTime();
					listOfWeekends.add(sat);
				} else if (daysOfWeek.get(from.get(Calendar.DAY_OF_WEEK)) == "Sun") {
					Date sun = from.getTime();
					listOfWeekends.add(sun);
				}
				from.add(Calendar.DAY_OF_MONTH, 1);
			}
		return listOfWeekends;
	}
	public static void main(String[] args) {
		String fromDate = "7-Oct-2019";
		String toDate = "25-Oct-2019";
		System.out.println(FindWeekendsInDateRange.calculateWeekendsInDateReange(new Date(fromDate), new Date(toDate)));
	

	}

}

答案 12 :(得分:0)

该计划考虑循环方法,但考虑下班后工作时间到下一个工作日办公时间开始的活动

公共类BusinessDayCalculator {

private final String DATE_FORMAT = "dd/MM/yyyy HH:mm:ss";
private final int OFFICE_START_HOUR = 9;
private final int OFFICE_CLOSE_HOUR = 17;
private final int TOTAL_MINS_IN_BUSINESS_DAY = (OFFICE_CLOSE_HOUR - OFFICE_START_HOUR)*60;

public void dateDifference(String start, String end){
    Date startDate = validateStringToDate(start);
    Date endDate = validateStringToDate(end);
    System.out.println(startDate);
    System.out.println(endDate);
    Calendar startDay = convertDateToCalendar(startDate);
    Calendar tempDay = (Calendar) startDay.clone();
    Calendar endDay = convertDateToCalendar(endDate);

    System.out.println(startDay.getTime());
    System.out.println(endDay.getTime());
    int workDays = -1;

    int startDayDifference = 0;
    int endDayDifference = 0;
    int hours = 0;
    int minsRemainder = 0;

    if(!(startDay.get(Calendar.DAY_OF_YEAR) == endDay.get(Calendar.DAY_OF_YEAR)
            && startDay.get(Calendar.YEAR) == endDay.get(Calendar.YEAR))){

        do{
            tempDay.add(Calendar.DAY_OF_MONTH, 1);
            if(tempDay.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY 
                    && tempDay.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY){
                workDays++;
            }
        }while(tempDay.getTimeInMillis() <= endDay.getTimeInMillis());

        if(workDays > 0){
            workDays = workDays - 1;
        }

    }

    startDayDifference = hourDifferenceInMinutesOfStartDay(startDay);
    endDayDifference = hourDifferenceInMinutesOfEndDay(endDay);

    minsRemainder = (startDayDifference + endDayDifference) % TOTAL_MINS_IN_BUSINESS_DAY;
    workDays = workDays + ((startDayDifference + endDayDifference) / TOTAL_MINS_IN_BUSINESS_DAY);

    hours = minsRemainder/60;
    minsRemainder = minsRemainder % 60;

    System.out.println(workDays + "d "+ hours + "hrs " + minsRemainder + " mins");

}


private int hourDifferenceInMinutesOfEndDay(Calendar endDay) {
    long endTimestamp = endDay.getTimeInMillis();
    System.out.println(endTimestamp);
    endDay.set(Calendar.HOUR_OF_DAY, OFFICE_START_HOUR);
    endDay.set(Calendar.MINUTE,0);
    long endDayOfficeStartTimestamp = endDay.getTimeInMillis();
    System.out.println(endDayOfficeStartTimestamp);
    int difference = (int)((endTimestamp - endDayOfficeStartTimestamp) / 1000) / 60;
    System.out.println(difference);
    return difference;
}


private int hourDifferenceInMinutesOfStartDay(Calendar startDay) {
    long starttimestamp = startDay.getTimeInMillis();
    System.out.println(starttimestamp);
    startDay.set(Calendar.HOUR_OF_DAY, OFFICE_CLOSE_HOUR);
    startDay.set(Calendar.MINUTE,0);
    long startDayOfficeCloseTimestamp = startDay.getTimeInMillis();
    System.out.println(startDayOfficeCloseTimestamp);
    int difference = (int)((startDayOfficeCloseTimestamp - starttimestamp) / 1000) / 60;
    System.out.println(difference);
    return difference;
}

public Calendar convertDateToCalendar(Date date){
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);

    if(calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY 
            || calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY){
        calendar = handleActivityOnAfterWorkHoursOrWeekendOrHolidays(calendar);
    }

    if(calendar.get(Calendar.HOUR_OF_DAY) >= OFFICE_CLOSE_HOUR 
            && calendar.get(Calendar.MINUTE) > 0){
        calendar = handleActivityOnAfterWorkHoursOrWeekendOrHolidays(calendar);
    }

    if(calendar.get(Calendar.HOUR_OF_DAY) < OFFICE_START_HOUR){
        calendar.set(Calendar.HOUR_OF_DAY, OFFICE_START_HOUR);
        calendar.set(Calendar.MINUTE,0);
    }

    return calendar;
}

private Calendar handleActivityOnAfterWorkHoursOrWeekendOrHolidays(Calendar calendar) {
    do{
        calendar.add(Calendar.DAY_OF_MONTH, 1);
    }while(isHoliday(calendar));
    calendar.set(Calendar.HOUR_OF_DAY, OFFICE_START_HOUR);
    calendar.set(Calendar.MINUTE,0);
    return calendar;
}

private boolean isHoliday(Calendar calendar) {
    if(calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY 
            || calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY){
        return true;
    }
    return false;
}

public Date validateStringToDate(String input){
    SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
    Date date = null;
    try{
        date = dateFormat.parse(input);
    }catch(ParseException exception){
        System.out.println("invalid date format");
        throw new RuntimeException("invalid date format");
    }
    return date;
}

public static void main(String[] args){
    BusinessDayCalculator calc = new BusinessDayCalculator();
    String startDate = "27/12/2016 11:38:00";
    String endDate = "04/01/2017 12:38:00";
    calc.dateDifference(startDate, endDate);
}

}

答案 13 :(得分:0)

这里是使用公式(wd2 - wd1 + 7)%7的任何工作日子集的解决方案,用于计算由数字1-7表示的任意两个工作日(wd1,wd2)之间的距离。

public long countOccurrences(LocalDate startDate, LocalDate endDate, Set<DayOfWeek> daysOfWeek) {
    long periodLength = ChronoUnit.DAYS.between(startDate, endDate) + 1;
    long fullWeeks = periodLength / 7;
    long residualWeekLength = periodLength % 7;

    return fullWeeks * daysOfWeek.size()
        + daysOfWeek.stream().mapToLong(
            // Yields either 1 or 0, depending on whether the residual week contains the target day or not.
            weekday -> 
            residualWeekLength > (weekday.getValue() - startDate.getDayOfWeek().getValue() + 7) % 7 ? 1 : 0     
        ).sum();
}

对于原始问题(周一至周五),它被称为例如用:

countOccurrences(LocalDate.of(2016, 2, 8), LocalDate.of(2016, 2, 26), new HashSet(Arrays.asList(DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY)))

答案 14 :(得分:0)

在groovy中:

  public static int getWorkingDaysBetweenDates (Date start, Date end) {
    def totalDays = (Integer) (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)
    def int workingDays = 0

    (0..totalDays).each { def dow = (start + it)[Calendar.DAY_OF_WEEK]; if(dow != Calendar.SATURDAY && dow != Calendar.SUNDAY){workingDays++} }

    workingDays
  }

答案 15 :(得分:0)

使用java 8可以轻松完成,示例功能:

long getBusinessDaysDifference(LocalDate startDate, LocalDate endDate) { 

    EnumSet<DayOfWeek> weekend = EnumSet.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);
    List<LocalDate> list = Lists.newArrayList();

    LocalDate start = startDate;        
    while (start.isBefore(endDate)) {
        list.add(start);
        start = start.plus(1, ChronoUnit.DAYS);
    }

    long numberOfDays = list.stream().filter(d -> !weekend.contains(d.getDayOfWeek())).count();

    return numberOfDays;
}

说明:

  1. off-days中定义您的EnumSet(在这种情况下为周末)。
  2. 创建一个列表,其中包含 startDate endDate 之间的所有日期。
  3. 通过从EnumSet中删除一天中发生的任何事情来减少结果列表。
  4. 然后最后算出此缩小列表的大小。

注意:此功能可以优化,但作为起点可能会有所帮助。

答案 16 :(得分:0)

这个线程充满了失败的解决方案...我首先编写了一个满足我的需求的测试文件,然后发现Roland的两个解决方案都失败了,Amir也失败了。我想要一个使用Java 8且不使用循环的解决方案,因为我必须说为什么?

这是测试文件:

@Test
public void test() {
    LocalDate d1 = LocalDate.of(2018, 8, 1);
    LocalDate d2 = LocalDate.of(2018, 8, 2);
    LocalDate d3 = LocalDate.of(2018, 8, 3);
    LocalDate d4 = LocalDate.of(2018, 8, 4);
    LocalDate d5 = LocalDate.of(2018, 8, 5);
    LocalDate d6 = LocalDate.of(2018, 8, 6);
    LocalDate d7 = LocalDate.of(2018, 8, 7);
    LocalDate d8 = LocalDate.of(2018, 8, 8);
    LocalDate d9 = LocalDate.of(2018, 8, 9);
    LocalDate d10 = LocalDate.of(2018, 8, 10);
    LocalDate d15 = LocalDate.of(2018, 8, 15);
    LocalDate dsep = LocalDate.of(2018, 9, 5);

    // same day : 0 days between
    Assert.assertEquals(0, DateUtils.calcWeekDays1(d1, d1));
    Assert.assertEquals(1, DateUtils.calcWeekDays1(d1, d2));
    Assert.assertEquals(2, DateUtils.calcWeekDays1(d1, d3));
    // end on week-end 
    Assert.assertEquals(2, DateUtils.calcWeekDays1(d1, d4));
    Assert.assertEquals(2, DateUtils.calcWeekDays1(d1, d5));
    // next week
    Assert.assertEquals(3, DateUtils.calcWeekDays1(d1, d6));
    Assert.assertEquals(4, DateUtils.calcWeekDays1(d1, d7));
    Assert.assertEquals(5, DateUtils.calcWeekDays1(d1, d8));
    Assert.assertEquals(6, DateUtils.calcWeekDays1(d1, d9));
    Assert.assertEquals(7, DateUtils.calcWeekDays1(d1, d10));
    // start on saturday
    Assert.assertEquals(0, DateUtils.calcWeekDays1(d4, d5));
    Assert.assertEquals(0, DateUtils.calcWeekDays1(d4, d6));
    Assert.assertEquals(1, DateUtils.calcWeekDays1(d4, d7));
    // start on sunday
    Assert.assertEquals(0, DateUtils.calcWeekDays1(d5, d5));
    Assert.assertEquals(0, DateUtils.calcWeekDays1(d5, d6));
    Assert.assertEquals(1, DateUtils.calcWeekDays1(d5, d7));
    // go to next week
    Assert.assertEquals(10, DateUtils.calcWeekDays1(d1, d15));
    // next month
    Assert.assertEquals(25, DateUtils.calcWeekDays1(d1, dsep));
    // start sat, go to next month
    Assert.assertEquals(22, DateUtils.calcWeekDays1(d4, dsep));

}

这是我建议的解决方案,非常简单。只需让java计算周数,乘以5,然后加上补偿差额所需的天数即可;唯一的技巧是调整开始和结束,以免出现周末:

public static long calcWeekDays1(LocalDate start, LocalDate end) {
    if (start.getDayOfWeek().getValue() > 5) {
        start = start.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
    }
    if (end.getDayOfWeek().getValue() > 5) {
        end = end.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
    }
    if (start.isAfter(end)) { // may happen if you start sat. and end sunday
        return 0;
    }
    long weeks = ChronoUnit.WEEKS.between(start, end);
    if (start.getDayOfWeek().getValue() > end.getDayOfWeek().getValue()) {
        weeks += 1;
    }
    return 5 * weeks + end.getDayOfWeek().getValue() - start.getDayOfWeek().getValue();
}

现在,如果我的代码也失败了,我会显得很愚蠢:)

答案 17 :(得分:0)

public long getNumberOfWeekDayBetweenDates(LocalDate startDate,LocalDate endDate,String dayOfWeek){         长结果= -1;         如果(startDate!= null && endDate!= null && dayOfWeek!= null &&(startDate.isBefore(endDate)|| startDate.isEqual(endDate))){             java.time.DayOfWeek namedDayOfWeek = java.time.DayOfWeek.valueOf(dayOfWeek);             //在间隔中找到星期的第一天             LocalDate firstOccurrence = startDate.with(TemporalAdjusters.nextOrSame(givenDayOfWeek));             //同样在上周一找到             LocalDate lastOccurrence = endDate.with(TemporalAdjusters.previousOrSame(givenDayOfWeek));             if(firstOccurrence!= null && lastOccurrence!= null){                 //计算第一次到最后一次出现之间的周数,然后加上1,因为结束日期是唯一的                 结果= ChronoUnit.WEEKS.between(firstOccurrence,lastOccurrence)+ 1;             } else if(firstOccurrence == null && lastOccurrence == null){                 //没有发生                 结果= 0;             }其他{                 结果= 1;             }         }         返回结果;     }

答案 18 :(得分:0)

无循环和包含间隔的Java 8解决方案:

public long getDaysWithoutSundays(LocalDate startDate, LocalDate endDate) {
    long numberOfDays = ChronoUnit.DAYS.between(startDate, endDate) + 1;
    long numberOfSundays = numberOfDays / 7;
    long rest = numberOfDays % 7;
    if (rest > 0) {
        int startToEnd = startDate.getDayOfWeek().getValue() - endDate.getDayOfWeek().getValue();
        if (startToEnd > 0) {
            numberOfSundays++;
        }
        else {
            if (endDate.getDayOfWeek().equals(DayOfWeek.SUNDAY)) {
                numberOfSundays++;
            }
        }
    }
    return numberOfDays - numberOfSundays;
}

答案 19 :(得分:0)

对于最新java版本支持的LocalDate,您可以尝试以下功能。

它提供对函数getDayOfWeek()的支持。

Java 中 getDayOfWeek() 类的 LocalDate 方法获取星期几字段,它是一个枚举 DayOfWeek。 >

public static int getWeekEndCount(LocalDate fromDate, LocalDate toDate) {
        int saturday = 0;
        int sunday = 0;
        while (!fromDate.isAfter(toDate)) {
            if (fromDate.getDayOfWeek().equals(DayOfWeek.SATURDAY))
                saturday++;
            else if (fromDate.getDayOfWeek().equals(DayOfWeek.SUNDAY))
                sunday++;
            fromDate = fromDate.plusDays(1);
        }
        System.out.println("Saturday count=="+saturday);
        System.out.println("Sunday count=="+sunday);
        return saturday+sunday;
    }