如何在 Java 中将工作日添加到当前日期?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1534804/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 13:55:25  来源:igfitidea点击:

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

javaalgorithm

提问by Anand

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

如何在 Java 中将工作日添加到当前日期?

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

Note:

笔记:

It should consider weekends too.

它也应该考虑周末。

采纳答案by shaolang

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

您可能需要考虑使用ObjectLab Kit为您完成繁重的工作。

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

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

库默认工作周是从周一到周五,但您可以通过向's提供自定义WorkingWeek来更改默认值。DateCalculatorsetWorkingWeek()

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

如最后两个示例所示,moveByDays()在移动日期时moveByBusinessDays()包括周末,而排除周末。

The library also allows you to use java.util.Calendaror Joda Time's LocalDate. The examples use JDK8's java.time.LocalDatebecause it is the preferred way since JDK8.

该库还允许您使用java.util.CalendarJoda TimeLocalDate。这些示例使用 JDK8,java.time.LocalDate因为它是自 JDK8 以来的首选方式。

Edit: Updated examples to use java.time.LocalDate

编辑:更新示例以使用 java.time.LocalDate

回答by OMG Ponies

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.

您必须填充日期列表才能处理假期。有像新年这样的常见节日,但例如加拿大和美国的感恩节是不同的。还要注意假期可能会在周末,所以周末变成了一个为期 3 天的周末。

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:

PS:如果您像示例中那样更新值,则实际上不需要返回 Calendar 实例。但是如果你想创建一个单独的 Calendar 实例,它是有效的,使用:

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

回答by Anand

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;

}

回答by rudolf

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

公共静态日期 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();
}

回答by JuanZe

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

回答by jetuns

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

回答by user2961462

//supports negative numbers too.

//也支持负数。

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

回答by garima soni

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

}

回答by jp093121

This one works for me, short and simple:

这个对我有用,简短而简单:

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

}

}

回答by Ezio Anselmo Mazarim Fernandes

O(1) versionthat works and supports different weekend patterns and negative days:

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