Может ли кто-нибудь указать мне какой-нибудь фрагмент Java, в котором я могу получить бизнес (за исключением СБ и ВС) дней между двумя датами.
Рассчитать количество будних дней между двумя датами в Java
Ответ 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
- Проект ThreeTenABP адаптирует 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;
}
Описание:
- Определите
off-days
вEnumSet
(в этом случае выходные). - Создайте список, содержащий все дни между startDate и endDate.
- Сократите список результатов, удалив любое вхождение дня из
EnumSet
. - Затем, наконец, посчитайте размер этого сокращенного списка.
Примечание: эта функция может быть оптимизирована, но может быть полезной в качестве отправной точки.
Ответ 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)));
}
}