/*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
* Copyright (c) 2014, MPL CodeInside http://codeinside.ru
*/
package ru.codeinside.calendar;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang.time.DateUtils;
import java.util.Calendar;
import java.util.Date;
import java.util.Set;
/**
* Рассчет окончания периода, длительность которого задана в рабочих днях.
*/
final public class BusinessCalendarDueDateCalculator implements DueDateCalculator {
final Set<Date> workedDays;
final Set<Date> holidays;
/**
* Конструктор калькулятора окончания периода
*
* @param workedDays даты дополнительных рабочих дней
* @param holidays даты дополнительных выходных дней
*/
public BusinessCalendarDueDateCalculator(Set<Date> workedDays, Set<Date> holidays) {
this.workedDays = ImmutableSet.copyOf(workedDays);
this.holidays = ImmutableSet.copyOf(holidays);
}
@Override
public Date calculate(Date startDate, int countDays) {
if (startDate == null) throw new IllegalArgumentException("Дата начала периода не должна быть NULL");
if (countDays < 0) throw new IllegalArgumentException("Длительность периода должна быть больше или равна нулю");
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
while (countDays > 0) {
calendar.add(Calendar.DAY_OF_MONTH, 1);
if (isWorkedDay(calendar)) {
countDays--;
}
}
return calendar.getTime();
}
/**
* Рассчет количества рабочих дней между двумя датами
*
* @param startDate дата начала периода
* @param endDate дата окончания периода
* @return количество рабочих дней между датами
*/
@Override
public int countDays(Date startDate, Date endDate) {
if (startDate == null) throw new IllegalArgumentException("Дата начала периода не должна быть NULL");
if (endDate == null) throw new IllegalArgumentException("Дата окончания периода не должна быть NULL");
boolean inverse = false;
if (endDate.before(startDate)) {
Date tmp = endDate;
endDate = startDate;
startDate = tmp;
inverse = true;
}
int countDays = 0;
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
for (; ; ) {
calendar.add(Calendar.DAY_OF_MONTH, 1);
if (calendar.getTime().after(endDate)) {
break;
}
if (isWorkedDay(calendar)) {
countDays++;
}
}
return inverse ? -countDays : countDays;
}
private boolean isWorkedDay(Calendar calendar) {
Date date = DateUtils.truncate(calendar.getTime(), Calendar.DAY_OF_MONTH);
boolean isHoliday = isWeekEnd(calendar) || holidays.contains(date);
boolean isWorkDay = workedDays.contains(date);
return !isHoliday || isWorkDay;
}
private boolean isWeekEnd(Calendar calendar) {
int day = calendar.get(Calendar.DAY_OF_WEEK);
return day == Calendar.SUNDAY || day == Calendar.SATURDAY;
}
}