Как добавить рабочие дни в текущую дату на Java?
public Calendar addBusinessDate(Calendar cal, int days) {
//
// code goes over here
//
}
Примечание:
Он также должен учитывать выходные дни.
Как добавить рабочие дни в текущую дату на Java?
public Calendar addBusinessDate(Calendar cal, int days) {
//
// code goes over here
//
}
Он также должен учитывать выходные дни.
Вы можете рассмотреть возможность использования ObjectLab Kit, чтобы сделать тяжелую работу за вас.
При условии, что требуется просто вернуть следующий рабочий день, когда вычисленная дата выпадает на нерабочий день:
package bizdays.example;
import java.time.LocalDate;
import java.util.HashSet;
import net.objectlab.kit.datecalc.common.DateCalculator;
import net.objectlab.kit.datecalc.common.DefaultHolidayCalendar;
import net.objectlab.kit.datecalc.common.HolidayHandlerType;
import net.objectlab.kit.datecalc.jdk8.LocalDateKitCalculatorsFactory;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
public class BizDayTest {
private DateCalculator<LocalDate> dateCalculator;
private final LocalDate startDate = LocalDate.of(2009, 12, 23);
@Before
public void setUp() {
HashSet<LocalDate> holidays = new HashSet<LocalDate>();
holidays.add(LocalDate.of(2009, 12, 25)); // Friday
DefaultHolidayCalendar<LocalDate> holidayCalendar =
new DefaultHolidayCalendar<LocalDate>(holidays);
LocalDateKitCalculatorsFactory.getDefaultInstance()
.registerHolidays("example", holidayCalendar);
dateCalculator = LocalDateKitCalculatorsFactory.getDefaultInstance()
.getDateCalculator("example", HolidayHandlerType.FORWARD);
dateCalculator.setStartDate(startDate);
}
@Test
public void should_not_change_calendar_start_date_even_after_moving() {
assertThat(
dateCalculator.moveByBusinessDays(6).getStartDate(),
equalTo(startDate));
}
@Test
public void moveByBusinessDays_will_return_24_dec_2009_as_next_business_day() {
assertThat(
dateCalculator.moveByBusinessDays(1).getCurrentBusinessDate(),
equalTo(LocalDate.of(2009, 12, 24)));
}
@Test
public void moveByBusinessDays_will_return_28_dec_2009_as_two_business_days_later() {
assertThat(
dateCalculator.moveByBusinessDays(2).getCurrentBusinessDate(),
equalTo(LocalDat.of(2009, 12, 28)));
}
@Test
public void moveByDays_will_also_return_28_dec_2009_as_two_business_days_later() {
assertThat(
dateCalculator.moveByDays(2).getCurrentBusinessDate(),
equalTo(LocalDate.of(2009, 12, 28)));
}
@Test
public void moveByBusinessDays_will_exclude_25_26_and_27_dec_when_computing_business_days() {
assertThat(
dateCalculator.moveByBusinessDays(5).getCurrentBusinessDate(),
equalTo(LocalDate.of(2009, 12, 31)));
}
@Test
public void moveByDays_will_include_25_26_and_27_dec_when_computing_business_days() {
assertThat(
dateCalculator.moveByDays(5).getCurrentBusinessDate(),
equalTo(LocalDate.of(2009, 12, 28)));
}
}
По умолчанию рабочая неделя библиотеки - с понедельника по пятницу, но вы можете изменить значения по умолчанию, WorkingWeek
собственный параметр WorkingWeek
для DateCalculator
setWorkingWeek()
.
Как показано в последних двух примерах, moveByDays()
включает выходные дни при перемещении дней, тогда как moveByBusinessDays()
исключает выходные дни.
Библиотека также позволяет использовать java.util.Calendar
или Joda Time LocalDate
. В примерах используется JDK8 java.time.LocalDate
потому что это предпочтительный способ начиная с JDK8.
Изменение: Обновлены примеры использования java.time.LocalDate
Использование:
public Calendar addBusinessDate(Calendar cal, int numBusinessDays) {
int numNonBusinessDays = 0;
for(int i = 0; i < numBusinessDays; i++) {
cal.add(Calendar.DATE, 1);
/*
It a Canadian/American custom to get the Monday (sometimes Friday) off
when a holiday falls on a weekend.
*/
for(int j = 0; j < holidays; j++) { //holidays is list of dates
if(cal.getTime() == (Date)holidays.get(j)) {
numNonBusinessDays++;
}
}
if(cal.get(Calendar.DAY_OF_WEEK) == 1 ||
cal.get(Calendar.DAY_OF_WEEK) == 7) {
numNonBusinessDays++;
}
}
if(numNonBusinessDays > 0) {
cal.add(Calendar.DATE, numNonBusinessDays);
}
return cal;
}
Вам нужно будет заполнить список дат для обработки праздников. Там такие общие, как Новый год, но День Благодарения отличается от Канады и США, например. Также помните, что праздники могут выпасть на выходные, поэтому выходные станут выходными на 3 дня.
Ссылка:
PS: Нет необходимости возвращать экземпляр календаря, если вы обновляете значение, как в примере. Но это действительно, если вы хотите создать отдельный экземпляр Календаря, используйте:
public Calendar addBusinessDate(Calendar cal, int numBusinessDays) {
Calendar cal2 = Calendar.getInstance();
cal2.setTime(cal.getTime());
int numNonBusinessDays = 0;
for(int i = 0; i < numBusinessDays; i++) {
cal2.add(Calendar.DATE, 1);
/*
It a Canadian/American custom to get the Monday (sometimes Friday) off
when a holiday falls on a weekend.
*/
for(int j = 0; j < holidays; j++) { //holidays is list of dates
if(cal2.getTime() == (Date)holidays.get(j)) {
numNonBusinessDays++;
}
}
if(cal2.get(Calendar.DAY_OF_WEEK) == 1 ||
cal2.get(Calendar.DAY_OF_WEEK) == 7) {
numNonBusinessDays++;
}
}
if(numNonBusinessDays > 0) {
cal2.add(Calendar.DATE, numNonBusinessDays);
}
return cal2;
}
Ниже приведена измененная версия для расчета даты.
public Calendar algorithm2(int businessDays){
Calendar cal2 = Calendar.getInstance();
Calendar cal = Calendar.getInstance();
int totalDays= businessDays/5*7;
int remainder = businessDays % 5;
cal2.add(cal2.DATE, totalDays);
switch(cal.get(Calendar.DAY_OF_WEEK)){
case 1:
break;
case 2:
break;
case 3:
if(remainder >3)
cal2.add(cal2.DATE,2);
break;
case 4:
if(remainder >2)
cal2.add(cal2.DATE,2);
break;
case 5:
if(remainder >1)
cal2.add(cal2.DATE,2);
break;
case 6:
if(remainder >1)
cal2.add(cal2.DATE,2);
break;
case 7:
if(remainder >1)
cal2.add(cal2.DATE,1);
break;
}
cal2.add(cal2.DATE, remainder);
return cal2;
}
public static Date addBusinessDays(Date date, int days) {
DateTime result = new DateTime(date);
result = isWeekEnd(result)
? getPreviousBusinessDate(result)
: result;
for (int i = 0; i < days; i++) {
if (isWeekEnd(result)) {
i--;
}
result = result.plusDays(1);
}
return result.toDate();
}
private static boolean isWeekEnd(DateTime dateTime) {
int dayOfWeek = dateTime.getDayOfWeek();
return dayOfWeek == DateTimeConstants.SATURDAY || dayOfWeek == DateTimeConstants.SUNDAY;
}
private static DateTime getPreviousBusinessDate(DateTime result) {
while (isWeekEnd(result)) {
result = result.minusDays(1);
}
return result;
}
//также поддерживает отрицательные числа.
private Calendar addBusinessDay(final Calendar cal, final Integer numBusinessDays)
{
if (cal == null || numBusinessDays == null || numBusinessDays.intValue() == 0)
{
return cal;
}
final int numDays = Math.abs(numBusinessDays.intValue());
final int dateAddition = numBusinessDays.intValue() < 0 ? -1 : 1;//if numBusinessDays is negative
int businessDayCount = 0;
while (businessDayCount < numDays)
{
cal.add(Calendar.DATE, dateAddition);
//check weekend
if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)
{
continue;//adds another day
}
//check holiday
if (isHoliday(cal))//implement isHoliday yourself
{
continue;//adds another day
}
businessDayCount++;
}
return cal;
}
Будет ли это работать? Конечно, это не обработка праздников.
публичная статическая дата addBusinessDays (Дата baseDate, int NumberOfDays) {
if(baseDate == null){ baseDate = new Date(); } Calendar baseDateCal = Calendar.getInstance(); baseDateCal.setTime(baseDate); for(int i = 0; i < numberOfDays; i++){ baseDateCal.add(Calendar.DATE,1); if(baseDateCal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY){ baseDateCal.add(Calendar.DATE,2); } } return baseDateCal.getTime(); }
Переход вперед.
myLocalDate.with(
org.threeten.extra.Temporals.nextWorkingDay()
)
Возвращение назад.
myLocalDate.with(
org.threeten.extra.Temporals.previousWorkingDay()
)
Вопросы и другие ответы используют неприятные старые классы времени, которые теперь устаревают, вытесняются классами java.time.
Также см. мой ответ на аналогичный вопрос.
TemporalAdjuster
В java.time интерфейс TemporalAdjuster
предоставляет классы для управления значениями даты и времени. Используя неизменяемые объекты, создается новый экземпляр со значениями, основанными на оригинале.
nextWorkingDay
Проект ThreeTen-Extra расширяет java.time с дополнительной функциональностью. Это включает nextWorkingDay
, который пропускает дни субботы и воскресенья. Таким образом, мы можем зацикливаться, увеличивая дату на один день за один раз и пропуская любые выходные дни.
Класс LocalDate
представляет значение даты только без времени и без часового пояса.
LocalDate start = LocalDate.now( ZoneId.of( "America/Montreal" ) ) ;
int businessDaysToAdd = 13 ;
// … ensure that: ( businessDaysToAdd >= 0 )
int daysLeft = businessDaysToAdd ;
LocalDate localDate = start ;
while ( daysLeft > 0 ) {
localDate = localDate.with( Temporals.nextWorkingDay() );
daysLeft = ( daysLeft - 1 ) ; // Decrement as we go.
}
return localDate ;
Праздники - совсем другое дело. Очевидно, нет простого решения. Вы должны либо предоставить список своих почетных праздников, либо получить список, с которым вы согласны.
Как только у вас появится такой список, я предлагаю написать собственную реализацию TemporalAdjuster
, аналогичную nextWorkingDay
.
Структура java.time встроена в Java 8 и более поздних версий. Эти классы вытесняют неприятные старые legacy классы времени, такие как java.util.Date
, Calendar
и SimpleDateFormat
.
Проект Joda-Time, теперь режим обслуживания, советуем перейти к классам java.time.
Чтобы узнать больше, см. Учебник Oracle. И поиск Qaru для многих примеров и объяснений. Спецификация JSR 310.
Где получить классы java.time?
Проект ThreeTen-Extra расширяет java.time с дополнительными классами. Этот проект является доказательством возможных будущих дополнений к java.time. Здесь вы можете найти полезные классы, такие как Interval
, YearWeek
, YearQuarter
и больше.
Этот алгоритм вычисляет следующую дату работы для данной даты (рабочие дни с понедельника по пятницу в моей стране), вы можете адаптировать его для повторения количества дней, которое нужно добавить.
public Calendar nextBusinessDate(Calendar cal) {
List<Calendar> holidays = ********
// Here get list of holidays from DB or some other service...
GregorianCalendar calCp = new GregorianCalendar();
calCp.setTime(cal.getTime());
calCp.add(Calendar.DAY_OF_MONTH, 1);
boolean isSaturday = (calCp.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY);
boolean isSunday = (calCp.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY);
boolean isHoliday = holidays.contains(calCp);
while (isSaturday || isSunday || isHoliday) {
if (isSaturday) {
calCp.add(Calendar.DAY_OF_MONTH, +2); // is saturday, make it monday
} else {
if (isSunday) {
calCp.add(Calendar.DAY_OF_MONTH, +1); // is sunday, make it monday
} else {
if (isHoliday) {
calCp.add(Calendar.DAY_OF_MONTH, +1); // is holiday, make it next day
}
}
}
calCp = new GregorianCalendar();
calCp.setTime(cal.getTime());
isSaturday = (calCp.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY);
isSunday = (calCp.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY);
isHoliday = holidays.contains(calCp);
} // end while
return calCp;
}
O (1) версия, которая работает и поддерживает разные шаблоны выходных дней и отрицательные дни:
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class DateUtil {
//Weekend patterns
public static final int WEEKEND_SAT_SUN = 0;
public static final int WEEKEND_FRI_SAT = 1;
public static final int WEEKEND_THU_FRI = 2;
public static final int WEEKEND_FRI_SUN = 3;
public static final int WEEKEND_FRI = 4;
public static final int WEEKEND_SAT = 5;
public static final int WEEKEND_SUN = 6;
//Weekend pattern by country
//@see https://en.wikipedia.org/wiki/Workweek_and_weekend
public static Map<String,Integer> weekendPatternByCountry = new HashMap<>();
static {
weekendPatternByCountry.put("CO",WEEKEND_SUN); //Colombia
weekendPatternByCountry.put("GQ",WEEKEND_SUN); //Equatorial Guinea
weekendPatternByCountry.put("IN",WEEKEND_SUN); //India
weekendPatternByCountry.put("MX",WEEKEND_SUN); //Mexico
weekendPatternByCountry.put("KP",WEEKEND_SUN); //North Korea
weekendPatternByCountry.put("UG",WEEKEND_SUN); //Uganda
weekendPatternByCountry.put("BN",WEEKEND_FRI_SUN); //Brunei Darussalam
weekendPatternByCountry.put("DJ",WEEKEND_FRI); //Djibouti
weekendPatternByCountry.put("IR",WEEKEND_FRI); //Iran
weekendPatternByCountry.put("AF",WEEKEND_THU_FRI); //Afghanistan
weekendPatternByCountry.put("NP",WEEKEND_SAT); //Nepal
weekendPatternByCountry.put("DZ",WEEKEND_FRI_SAT); //Algeria
weekendPatternByCountry.put("BH",WEEKEND_FRI_SAT); //Bahrain
weekendPatternByCountry.put("BD",WEEKEND_FRI_SAT); //Bangladesh
weekendPatternByCountry.put("EG",WEEKEND_FRI_SAT); //Egypt
weekendPatternByCountry.put("IQ",WEEKEND_FRI_SAT); //Iraq
weekendPatternByCountry.put("IL",WEEKEND_FRI_SAT); //Israel
weekendPatternByCountry.put("JO",WEEKEND_FRI_SAT); //Jordan
weekendPatternByCountry.put("KW",WEEKEND_FRI_SAT); //Kuwait
weekendPatternByCountry.put("LY",WEEKEND_FRI_SAT); //Libya
weekendPatternByCountry.put("MV",WEEKEND_FRI_SAT); //Maldives
weekendPatternByCountry.put("MR",WEEKEND_FRI_SAT); //Mauritania
weekendPatternByCountry.put("MY",WEEKEND_FRI_SAT); //Malaysia
weekendPatternByCountry.put("OM",WEEKEND_FRI_SAT); //Oman
weekendPatternByCountry.put("PS",WEEKEND_FRI_SAT); //Palestine
weekendPatternByCountry.put("QA",WEEKEND_FRI_SAT); //Qatar
weekendPatternByCountry.put("SA",WEEKEND_FRI_SAT); //Saudi Arabia
weekendPatternByCountry.put("SD",WEEKEND_FRI_SAT); //Sudan
weekendPatternByCountry.put("SY",WEEKEND_FRI_SAT); //Syria
weekendPatternByCountry.put("AE",WEEKEND_FRI_SAT); //United Arab Emirates
weekendPatternByCountry.put("YE",WEEKEND_FRI_SAT); //Yemen
}
//Adjustment vectors - precomputed adjustment
static int[][][] adjVector = new int[][][]{
{//WEEKEND_SAT_SUN
//Positive number of days
{1,0,-1,-2,-3,1,1},
{0,0},
{0,0,0,0,0,2,1},
//Negative number of days
{-1,3,2,1,0,-1,-1},
{0,0},
{-1,1,1,1,1,1,0}
},
{//WEEKEND_FRI_SAT
//Positive number of days
{0,-1,-2,-3,1,1,1},
{0,0},
{0,0,0,0,2,1,0},
//Negative number of days
{3,2,1,0,-1,-1,-1},
{0,0},
{1,1,1,1,1,0,-1}
},
{//WEEKEND_THU_FRI
//Positive number of days
{-1,-2,-3,1,1,1,0},
{0,0},
{0,0,0,2,1,0,0},
//Negative number of days
{2,1,0,-1,-1,-1,3},
{0,0},
{1,1,1,1,0,-1,1}
},
{//WEEKEND_FRI_SUN
//Positive number of days
{0,-1,-2,-3,-4,-4,0},
{1,0},
{0,0,0,0,0,-1,1},
//Negative number of days
{4,3,2,1,0,0,4},
{0,-1},
{1,1,1,1,1,0,2}
},
{//WEEKEND_FRI
//Positive number of days
{-1,-2,-3,-4,1,1,0},
{0},
{0,0,0,0,1,0,0},
//Negative number of days
{3,2,1,0,-1,-1,4},
{0},
{1,1,1,1,1,0,1}
},
{//WEEKEND_SAT
//Positive number of days
{0,-1,-2,-3,-4,1,1},
{0},
{0,0,0,0,0,1,0},
//Negative number of days
{4,3,2,1,0,-1,-1},
{0},
{1,1,1,1,1,1,0}
},
{//WEEKEND_SUN
//Positive number of days
{1,0,-1,-2,-3,-4,1},
{0},
{0,0,0,0,0,0,1},
//Negative number of days
{-1,4,3,2,1,0,-1},
{0},
{0,1,1,1,1,1,1}
}
};
//O(1) algorithm to add business days.
public static Date addBusinessDays(Date day, int days,int weekendPattern){
Calendar ret = Calendar.getInstance();
if(day != null) {
ret.setTime(day);
}
if(days != 0) {
int startDayofWeek = ret.get(Calendar.DAY_OF_WEEK)-1; //Zero based to use the vectors bellow.
int idx = days > 0 ? 0 : 3;
int howManyWeekendDays = 0;
int[][] adjV = adjVector[weekendPattern];
int numWeekendDaysInOneWeek = adjV[idx+1].length;
for(int i = 0; i < numWeekendDaysInOneWeek;i++){
int adjustmentA = adjV[idx][startDayofWeek]; //pattern shift
int adjustmentB = adjV[idx+1][i]; //day shift
howManyWeekendDays += (days-adjustmentA-adjustmentB)/(7-numWeekendDaysInOneWeek);
}
int adjustmentC = adjV[idx+2][startDayofWeek]; //f(0) adjustment
howManyWeekendDays += adjustmentC;
ret.add(Calendar.DATE,days + howManyWeekendDays);
//TODO: Extend to support holidays using recursion
// int numHolidays = getNumHolidaysInInterval(day,ret.getTime());
// if(numHolidays > 0) return addBusinessDays(ret.getTime,numHolidays);
}
return ret.getTime();
}
public static Date addBusinessDays(Date day, int days,String country){
Integer weekpat = weekendPatternByCountry.get(country);
return weekpat != null ? addBusinessDays(day,days,weekpat) : addBusinessDays(day,days,WEEKEND_SAT_SUN);
}
}
Это метод, который я придумал:
private Date addLaborDays(Integer days, Date date){
Collection<Date> holidaysList = getHolidays();
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DATE, 1);
Date dateTemp = cal.getTime();
if(days == 1) return dateTemp;
if(holidaysList.contains(dateTemp) || DateUtil.isWeekend(dateTemp)){
return addLaborDays(days, dateTemp);
} else {
return addLaborDays(days-1, dateTemp);
}
}
Метод getHolidays()
запрашивает пользовательскую таблицу базы данных выходных дней, а метод DateUtil.isWeekend(dateTemp)
возвращает значение true, если dateTemp - суббота или воскресенье.
Пожалуйста, смотрите http://jollyday.sourceforge.net, если вы хотите, чтобы API определял праздники. Это будет прекрасно работать вместе с API-интерфейсом objectlabkit.
/* To Calculate 10 business days ahead of today date
*/
public class DueDate {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
DueDate d = new DueDate();
String dueDate = d.getDueDate(10);
System.out.println("due Date " + dueDate);
}
public String getDueDate(int bday){
Calendar cal = new GregorianCalendar();
SimpleDateFormat fdate = new SimpleDateFormat("MM/dd/yyyy");
while(bday > 0){
cal.add(Calendar.DAY_OF_MONTH, 1);
if(noWeekendsorHolidays(cal)){
bday--;
}
}
return fdate.format(cal.getTime());
}
public boolean noWeekendsorHolidays(Calendar cal){
int day = cal.get(Calendar.DAY_OF_WEEK);
if(day == 1 || day == 7){
return false;
}
return true;
}
}
Это работает для меня коротко и просто:
public static Date getBusinessDay(final Date date, final int businessDaysFromDate) {
final int max = 60;
if (date == null) {
return getBusinessDay(new Date(), businessDaysFromDate);
} else if (date != null && (businessDaysFromDate < 0 || businessDaysFromDate > max)) {
return getBusinessDay(date, 0);
} else {
final Calendar baseDateCal = Calendar.getInstance();
baseDateCal.setTime(date);
for (int i = 1; i <= businessDaysFromDate; i++) {
baseDateCal.add(Calendar.DATE, 1);
while (baseDateCal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || baseDateCal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
baseDateCal.add(Calendar.DATE, 1);
}
}
return baseDateCal.getTime();
}
}
Добавление двух рабочих дней к текущей дате:
Date today = new Date();
Calendar cal1 = Calendar.getInstance();
cal1.setTime(today);
switch(cal1.get(Calendar.DAY_OF_WEEK)){
case 1:
cal1.add(Calendar.DATE, 2);
break;
case 2:
cal1.add(Calendar.DATE, 2);
break;
case 3:
cal1.add(Calendar.DATE, 2);
break;
case 4:
cal1.add(Calendar.DATE, 2);
break;
case 5:
cal1.add(Calendar.DATE, 4);
break;
case 6:
cal1.add(Calendar.DATE, 4);
break;
case 7:
cal1.add(Calendar.DATE, 3);
break;
}
// You may also set the time to meet your purpose:
cal1.set(Calendar.HOUR_OF_DAY, 23);
cal1.set(Calendar.MINUTE, 59);
cal1.set(Calendar.SECOND, 59);
cal1.set(Calendar.MILLISECOND, 00);
Date twoWeekdaysAhead = cal1.getTime();
Большинство ответов, которые я нашел в Интернете, не сработали, как ожидалось, поэтому я подправил пример в этой теме: Как получить текущую дату и добавить пять рабочих дней в Java. Код ниже, кажется, работает лучше.
public static Date addWorkingDays(Date date, int days) {
if (days > 0) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int daysAdded = 0;
do {
cal.add(Calendar.DATE, 1);
if (isWorkingDay(cal)) {
daysAdded++;
}
} while (daysAdded < days);
return cal.getTime();;
} else {
return date;
}
}
private static boolean isWorkingDay(Calendar cal) {
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
if (dayOfWeek == Calendar.SUNDAY || dayOfWeek == Calendar.SATURDAY)
return false;
// tests for other holidays here
return true;
}