tags:

views:

1505

answers:

5

How can I add business days to the current date in Java?

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

Note:

It should consider weekends too.

+1  A: 

Use:

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

You'd have to populate a list of dates in order to handle holidays. There's common ones like New Years, but Thanksgiving is different between Canada & the US for instance. Also mind that holidays can fall on a weekend, so the weekend becomes a 3 day weekend.

Reference:

PS: There isn't really a need to return the Calendar instance if you are updating the value as in the example. But it is valid if you want to create a separate Calendar instance, 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;
}
OMG Ponies
So January 1st, 2009 is a business day because it happens to fall on Thursday?
ChssPly76
You'd think so, but no :-) Checking date equality via '==' is not going to work.
ChssPly76
I don't think that's what you meant to do because current version won't compile (assignment to function result _and_ within if condition). My point was that you can't check date equality by reference. Incidentally, for what you're trying to do `equals()` is not going to work either due to time portion. I'm upvoting you for the effort put in :-) and I suggest you let the OP figure out the rest of this.
ChssPly76
@OMG Ponies: why add numNonBusinessDays but use a loop to add 1 for each numBusinessDays?
Otis
also if an holiday falls on weekday 1 or 7 (better use constants: `Calendar.SATERDAY`, ...), we get 2 days added...
Carlos Heuberger
@Carlos: Because when a holiday falls on a weekend, it's a Canadian/American custom to get the Monday (sometimes Friday) off.
OMG Ponies
@Otis: Because you have to loop through the list of holiday dates to compare to the current date value in order to determine if it is a holiday. At least, I think that is what your question is about - I'm unsure.
OMG Ponies
that's a nice *rule*... we don't have that in Brazil or Germany :-/ but, anyway, found a problem: `cal2.get(Calendar.DAY_OF_WEEK) = 1` won't work, should be `==`
Carlos Heuberger
@Carlos: It's already been mentioned that holidays are regional; holiday handling understandably would be too. I never said the example covered all conventions but I look forward to see yours that does. I have to wonder how the accepted answer, encapsulating the logic, handles such regional differences...
OMG Ponies
@OMG Ponies: just wanted to help (and understand), sorry if you got that wrong...
Carlos Heuberger
+6  A: 

You may want to consider using ObjectLab Kit to do the heavy lifting for you.

Assuming the requirement is simply to return the next business day when the computed date falls on a non-business day:

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<LocalDate> dateCalculator;
    private final LocalDate startDate = new LocalDate(2009, 12, 23);

    @Before
    public void setUp() {
        HashSet<LocalDate> holidays = new HashSet<LocalDate>();
        holidays.add(new LocalDate(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(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)));
    }
}

The library defaults the working week to be from Monday to Friday, but you can change the defaults by supplying a custom WorkingWeek to DateCalculator's setWorkingWeek().

As shown in the last two examples, moveByDays() includes the weekends when moving the days, whereas moveByBusinessDays() excludes weekends.

The library also allows you to use java.util.Calendar instead of Joda Time's LocalDate. The example uses Joda Time library because it is the preferred library to use when handling dates in Java.

shaolang
An example of how to use it is always more welcome than just saying "use x".
Otis
A: 

Here is the modified version to find date calculation.

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;

}

Blockquote

Anand
Where's the holiday handling?
OMG Ponies
what is `case 1:`? Easier to read if you use the constants `case Calendar.SUNDAY:`...
Carlos Heuberger
+1  A: 

Will this work? Of course, this is not handling holidays.

public static Date 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();
}
rudolf
note: not correct if `baseDate` is a saturday...not sure if that can happen (murphy's law...)
Carlos Heuberger
A: 

This algorithm calculates the next business date for a given date (business days are from monday to friday in my country), you can adapt it to iterate the number of days you need to add.

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;
}
JuanZe
I think that `holidays.contains()` will not work. The `Calendar.equals()` (also) use milliseconds to compare the dates.
Carlos Heuberger