Подтвердить что ты не робот

Рассчитать количество будних дней между двумя датами в Java

Может ли кто-нибудь указать мне какой-нибудь фрагмент Java, в котором я могу получить бизнес (за исключением СБ и ВС) дней между двумя датами.

4b9b3361

Ответ 1

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

Решение без цикла:

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;
}

Ответ 3

Решение без цикла в 5 строк кода

Дни между определены так же, как ChronoUnit.DAYS.between(start, end) что означает, что между понедельником и пятницей есть 4 дня. Поскольку нас интересуют только рабочие дни, мы должны вычитать выходные, поэтому с пятницы на вторник будет 2 endDay - startDay (просто вычислите endDay - startDay и 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;
}

Ответ 4

Я использовал решение Shengyuan Lu, но мне нужно было исправить случай, когда метод вызывается, когда одна из дат находится в субботу, а другая - в воскресенье - в противном случае ответ отключается на день:

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;
}

Ответ 5

java.time

Современный способ - с классами java.time.

LocalDate

Класс LocalDate представляет значение только для даты без времени суток и без часового пояса.

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

DayOfWeek

DayOfWeek перечисление предоставляет одноэлементный экземпляр для каждого из дней разорвать недели.

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 - это чрезвычайно эффективная, быстрая и не 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 регулятор 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.

Вот четыре метода, каждый из которых возвращает количество прошедших дней.

Один использует умный математический подход, который можно увидеть в ответе Роланда.

private long countWeekDaysMath ( LocalDate start , LocalDate stop ) {
    // Code taken from Answer by Roland.
    // /questions/142173/calculate-number-of-weekdays-between-two-dates-in-java/840742#840742
    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;
}

Последний использует подход Java Streams, представленный в Ответе Равиндры Ранвала.

private long countWeekDaysStream ( LocalDate start , LocalDate stop ) {
    // Code taken from the Answer by Ravindra Ranwala.
    // /questions/142173/calculate-number-of-weekdays-between-two-dates-in-java/26981555#26981555
    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.
  • Я не удосужился попробовать оптимизировать любой из этого кода. Например, в реальной работе 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 ) );

При запуске на моем MacBook Pro (Sierra) с Oracle JDK 10.0.1 и ThreeTen-Extra версии 1.3.2 я получаю результаты, практически совпадающие со следующими. Математическое решение - крошечная доля других в паре сотен нано против нескольких тысяч, как и следовало ожидать. Из трех других, TemporalAdjuster является самым длинным, всегда более 10000 нанометров каждый. Посещение и стрим оба достигают гораздо меньше 10000 нанометров каждый, при этом посещение заметно быстрее, чем стримы. Как видно из других примеров, связанных с интернет- сетями, потоки Java обычно создают изящный короткий код, но при этом часто работают значительно дольше, в этом случае примерно на 20% дольше.

математика: 18313309 каждая: 183

визит: 708420626 каждый: 7084

Регулятор: 1002157240 каждый: 10021

поток: 924724750 каждый: 9247


О java.time

Инфраструктура java.time встроена в Java 8 и более поздние версии. Эти классы вытеснять неприятные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar, и SimpleDateFormat.

Проект Joda-Time, находящийся сейчас в режиме обслуживания, рекомендует перейти на java.time.

Чтобы узнать больше, смотрите Oracle Tutorial. И поиск для многих примеров и объяснений. Спецификация JSR 310.

Где взять классы java.time?

  • Java SE 8 и SE 9 и позже
    • Встроенный.
    • Часть стандартного Java API с комплексной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и SE 7
    • Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport.
  • Android

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти некоторые полезные классы, такие как Interval, YearWeek, YearQuarter и другие.

Ответ 6

У меня нет решения на базе 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()));
}

Ответ 7

Почти все решения в значительной степени устарели и повествовательны. Однако здесь очень сжатое и читаемое решение.

Этот подход использует поток Java, предоставляемый методом LocalDate::datesUntil встроенным в Java 9 и более поздние LocalDate::datesUntil.

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();

.datesUntil возвращает упорядоченный поток дат. Возвращенный поток начинается с этой даты (включительно) и переходит в endExclusive (эксклюзив) с шагом 1 день.

Затем все субботы и воскресенья отфильтровываются. Последний шаг - подсчет оставшихся дней недели.

Java-9 был выпущен год назад, так как его использование сейчас кажется мне разумным.

Ответ 8

do while в решении Piyush ошибочно, это должно быть:

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

Это мой пример без цикла. Алгоритм такой же, как 卢 声 远 Shengyuan Lus, но я использовал некоторые функции JodaTime.

public static int getNumberOfBusinessDays(@Nonnull LocalDate from, @Nonnull LocalDate to) {
    int fromDateDayOfWeek = from.getDayOfWeek();
    int toDateDayOfWeek = to.getDayOfWeek();

    int daysWithoutWeekends = 5 * Weeks.weeksBetween(
            from.withDayOfWeek(DateTimeConstants.MONDAY), to).getWeeks();

    if (fromDateDayOfWeek == DateTimeConstants.SUNDAY) {
        fromDateDayOfWeek = DateTimeConstants.SATURDAY;
    }
    if (toDateDayOfWeek == DateTimeConstants.SUNDAY) {
        toDateDayOfWeek = DateTimeConstants.SATURDAY;
    }

    return daysWithoutWeekends - (fromDateDayOfWeek - toDateDayOfWeek);
}

Ответ 10

startCal.add следует добавить в поле Calendar.DATE, а не Calendar.DAY_OF_MONTH, я получал странные результаты с периодом за декабрь/январь.

Ответ 11

Это мой пример без цикла. Это класс в этом примере, потому что я сериализую его в некоторых выводах JSON. В основном я определяю количество дней между двумя датами, деля на 7 и назначаю длинное целое значение для количества недель. Возьмите первоначальное количество дней и вычтите количество выходных дней * 2. Это не совсем идеально - вам нужно поработать, если есть "похмелье", где начало близко к концу недели и проходит в выходные дни. Чтобы исправить это, я нахожу день недели в начале и нахожу оставшуюся часть числа дней, и добавляю их вместе, чтобы найти "похмелье", - и если его больше 5, это выходные. Он не совсем совершенен и вообще не учитывает праздники. И нет Джоды в поле зрения. Тем не менее, существует также проблема с часовыми поясами.

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;
    }

}

Ответ 12

Эта программа рассматривает подход цикла, но рассматривает действия, произошедшие после рабочего времени, до следующего рабочего часа в офисе рабочего дня.

открытый класс 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

Здесь есть решение для любого поднабора в будние дни, использующего формулу (wd2 - wd1 + 7)% 7 для вычисления расстояния между любыми двумя будничными днями (wd1, wd2), представленными цифрами 1-7.

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

В 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

Используя 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

Эта тема заполнена ошибочными решениями... Я начал с того, что написал небольшой тестовый файл, который отвечал моим потребностям, и увидел, что оба решения Роланда дают сбой, Амир тоже. Я хотел решение, которое использует 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 посчитать количество недель, умножьте на пять и добавьте количество дней, необходимое для компенсации разницы; единственная хитрость - настроить начало и конец, чтобы избежать выходных:

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

public long getNumberOfWeekDayBetweenDates(LocalDate startDate, LocalDate endDate, String dayOfWeek) {
        long result = -1;
        if (startDate != null && endDate != null && dayOfWeek != null && (startDate.isBefore(endDate) || startDate.isEqual(endDate))) {
            java.time.DayOfWeek givenDayOfWeek = java.time.DayOfWeek.valueOf(dayOfWeek);
            // find the first given day of week in the interval
            LocalDate firstOccurrence = startDate.with(TemporalAdjusters.nextOrSame(givenDayOfWeek));
            // similarly find last Monday
            LocalDate lastOccurrence = endDate.with(TemporalAdjusters.previousOrSame(givenDayOfWeek));
            if (firstOccurrence != null && lastOccurrence != null) {
                // count the number of weeks between the first and last occurrence, then add 1 as end day is exclusive
                result = ChronoUnit.WEEKS.between(firstOccurrence, lastOccurrence) + 1;
            } else if (firstOccurrence == null && lastOccurrence == null) {
                // no occurrence
                result = 0;
            } else {
                result = 1;
            }
        }
        return result;
    }

Ответ 18

Решение для 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

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)));
	

	}

}