Como posso adicionar dias úteis à data atual em Java?

Como posso adicionar dias úteis à data atual em Java?

public Calendar addBusinessDate(Calendar cal, int days) { // // code goes over here // } 

Nota:

Deve considerar os finais de semana também.

Você pode querer considerar o uso do Kit ObjectLab para fazer o trabalho pesado para você.

Supondo que o requisito é simplesmente retornar no próximo dia útil, quando a data computada cair em um dia não comercial:

 package bizdays.example; 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.joda.LocalDateKitCalculatorsFactory; import static org.junit.Assert.assertThat; import org.junit.Before; import org.junit.Test; import static org.hamcrest.Matchers.equalTo; import org.joda.time.LocalDate; public class BizDayTest { private DateCalculator dateCalculator; private final LocalDate startDate = new LocalDate(2009, 12, 23); @Before public void setUp() { HashSet holidays = new HashSet(); holidays.add(new LocalDate(2009, 12, 25)); // Friday DefaultHolidayCalendar holidayCalendar = new DefaultHolidayCalendar(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(new LocalDate(2009, 12, 24))); } @Test public void moveByBusinessDays_will_return_28_dec_2009_as_two_business_days_later() { assertThat( dateCalculator.moveByBusinessDays(2).getCurrentBusinessDate(), equalTo(new LocalDate(2009, 12, 28))); } @Test public void moveByDays_will_also_return_28_dec_2009_as_two_business_days_later() { assertThat( dateCalculator.moveByDays(2).getCurrentBusinessDate(), equalTo(new LocalDate(2009, 12, 28))); } @Test public void moveByBusinessDays_will_exclude_25_26_and_27_dec_when_computing_business_days() { assertThat( dateCalculator.moveByBusinessDays(5).getCurrentBusinessDate(), equalTo(new LocalDate(2009, 12, 31))); } @Test public void moveByDays_will_include_25_26_and_27_dec_when_computing_business_days() { assertThat( dateCalculator.moveByDays(5).getCurrentBusinessDate(), equalTo(new LocalDate(2009, 12, 28))); } } 

A biblioteca padroniza a semana de trabalho de segunda a sexta-feira, mas você pode alterar os padrões fornecendo um WorkWeek personalizado para o setWorkingWeek () da DateCalculator .

Como mostrado nos dois últimos exemplos, moveByDays () inclui os finais de semana ao mover os dias, enquanto moveByBusinessDays () exclui os finais de semana.

A biblioteca também permite que você use o java.util.Calendar em vez do LocalDate do Joda Time . O exemplo usa a biblioteca Joda Time porque é a biblioteca preferida para usar ao manipular datas em Java.

Usar:

 public Calendar addBusinessDate(Calendar cal, int numBusinessDays) { int numNonBusinessDays = 0; for(int i = 0; i < numBusinessDays; i++) { cal.add(Calendar.DATE, 1); /* It's 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; } 

Você teria que preencher uma lista de datas para lidar com feriados. Há coisas comuns como o Ano Novo, mas o Dia de Ação de Graças é diferente entre o Canadá e os EUA, por exemplo. Lembre-se também que as férias podem cair em um fim de semana, então o fim de semana se torna um fim de semana de 3 dias.

Referência:

  • Calendário
  • Valores constantes do calendar

PS: Não há realmente a necessidade de retornar a instância do Google Agenda se você estiver atualizando o valor como no exemplo. Mas, se você quiser criar uma instância do Google Agenda separada, use:

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

Aqui está a versão modificada para encontrar o cálculo da data.

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

// suporta números negativos também.

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

Isso vai funcionar? Claro, isso não está lidando com feriados.

data estática pública addBusinessDays (Date 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(); } 

tl; dr

Daqui para frente.

 myLocalDate.with( org.threeten.extra.Temporals.nextWorkingDay() ) 

Indo para trás.

 myLocalDate.with( org.threeten.extra.Temporals.previousWorkingDay() ) 

Usando java.time

A pergunta e outras respostas usam as antigas classs problemáticas de data e hora, agora legadas, suplantadas pelas classs java.time.

Além disso, veja minha resposta a uma pergunta semelhante.

TemporalAdjuster

Em java.time, a interface TemporalAdjuster fornece classs para manipular valores de data e hora. Usando objects imutáveis , uma nova instância é criada com valores baseados no original.

nextWorkingDay

O projeto ThreeTen-Extra estende java.time com funcionalidade adicional. Isso inclui um ajustador nextWorkingDay que pula durante os dias de sábado e domingo. Assim, podemos fazer um loop, incrementar uma data um dia de cada vez e pular todos os dias de fim de semana.

A class LocalDate representa um valor somente de datas sem hora do dia e sem fuso horário.

 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 ; 

Feriados

As férias são um assunto completamente diferente. Obviamente, não há solução simples. Você deve fornecer uma lista de seus feriados de honra ou obter uma lista com a qual você concorda.

Depois de ter essa lista, sugiro escrever sua própria implementação de TemporalAdjuster semelhante ao nextWorkingDay .


Sobre o java.time

O framework java.time está embutido no Java 8 e posterior. Essas classs substituem as antigas classs herdadas de data e hora legadas , como java.util.Date , Calendar e SimpleDateFormat .

O projeto Joda-Time , agora em modo de manutenção , aconselha a migration para as classs java.time .

Para saber mais, veja o Oracle Tutorial . E pesquise o Stack Overflow para muitos exemplos e explicações. A especificação é JSR 310 .

Onde obter as classs java.time?

  • Java SE 8 e SE 9 e posterior
    • Construídas em.
    • Parte da API Java padrão com uma implementação integrada.
    • O Java 9 adiciona alguns resources e correções menores.
  • Java SE 6 e SE 7
    • Grande parte da funcionalidade java.time é transferida para o Java 6 e 7 no ThreeTen-Backport .
  • Android
    • O projeto ThreeTenABP adapta o ThreeTen-Backport (mencionado acima) especificamente para o Android.
    • Veja Como usar o ThreeTenABP… .

O projeto ThreeTen-Extra estende java.time com classs adicionais. Este projeto é um campo de testes para possíveis futuras adições ao java.time. Você pode encontrar algumas classs úteis aqui como Interval , YearWeek , YearQuarter e muito mais .

Este algoritmo calcula a próxima data de negócios para uma determinada data (os dias úteis são de segunda a sexta-feira no meu país) , você pode adaptá-lo para repetir o número de dias que você precisa adicionar.

 public Calendar nextBusinessDate(Calendar cal) { List 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) versão que funciona e suporta diferentes padrões de fim de semana e dias negativos:

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

Este é o método que eu criei:

  private Date addLaborDays(Integer days, Date date){ Collection 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); } } 

O método getHolidays() consulta uma tabela de database de feriados personalizada e o método DateUtil.isWeekend(dateTemp) retorna true se dateTemp for Saturday ou Sunday.

 /* To Calculate 10 business days ahead of today's 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; } } 

Este funciona para mim, curto e simples:

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

}

Adicionando dois dias úteis à data atual:

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