如何在Java 8中向LocalDate添加天数时跳过周末?

Anm*_*pta 21 java algorithm java-time

这里的其他答案指的是Joda API.我想用它来做java.time.

假设今天的日期是2015年11月26日 - 星期四,当我增加2个工作日时,我希望结果为2015年11月30日星期一.

我正在开发自己的实现,但如果已经存在的话会很棒!

编辑:

除了循环之外,有没有办法做到这一点?

我试图得到一个像这样的函数:

Y = f(X1,X2) where
Y is actual number of days to add,
X1 is number of business days to add, 
X2 is day of the week (1-Monday to 7-Sunday)
Run Code Online (Sandbox Code Playgroud)

然后给出X1X2(从日期的星期几得出),我们可以找到Y然后使用plusDays()方法LocalDate.

到目前为止,我还没有得出它,它不一致.任何人都可以确认循环,直到添加所需的工作日数是唯一的方法吗?

Mod*_*ens 25

以下方法逐个添加天数,跳过周末,正值为workdays:

public LocalDate add(LocalDate date, int workdays) {
    if (workdays < 1) {
        return date;
    }

    LocalDate result = date;
    int addedDays = 0;
    while (addedDays < workdays) {
        result = result.plusDays(1);
        if (!(result.getDayOfWeek() == DayOfWeek.SATURDAY ||
              result.getDayOfWeek() == DayOfWeek.SUNDAY)) {
            ++addedDays;
        }
    }

    return result;
}
Run Code Online (Sandbox Code Playgroud)

经过一番摆弄后,我想出了一个算法来计算要加或减的工作日数.

/**
 * @param dayOfWeek
 *            The day of week of the start day. The values are numbered
 *            following the ISO-8601 standard, from 1 (Monday) to 7
 *            (Sunday).
 * @param businessDays
 *            The number of business days to count from the day of week. A
 *            negative number will count days in the past.
 * 
 * @return The absolute (positive) number of days including weekends.
 */
public long getAllDays(int dayOfWeek, long businessDays) {
    long result = 0;
    if (businessDays != 0) {
        boolean isStartOnWorkday = dayOfWeek < 6;
        long absBusinessDays = Math.abs(businessDays);

        if (isStartOnWorkday) {
            // if negative businessDays: count backwards by shifting weekday
            int shiftedWorkday = businessDays > 0 ? dayOfWeek : 6 - dayOfWeek;
            result = absBusinessDays + (absBusinessDays + shiftedWorkday - 1) / 5 * 2;
        } else { // start on weekend
            // if negative businessDays: count backwards by shifting weekday
            int shiftedWeekend = businessDays > 0 ? dayOfWeek : 13 - dayOfWeek;
            result = absBusinessDays + (absBusinessDays - 1) / 5 * 2 + (7 - shiftedWeekend);
        }
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

LocalDate startDate = LocalDate.of(2015, 11, 26);
int businessDays = 2;
LocalDate endDate = startDate.plusDays(getAllDays(startDate.getDayOfWeek().getValue(), businessDays));

System.out.println(startDate + (businessDays > 0 ? " plus " : " minus ") + Math.abs(businessDays)
        + " business days: " + endDate);

businessDays = -6;
endDate = startDate.minusDays(getAllDays(startDate.getDayOfWeek().getValue(), businessDays));

System.out.println(startDate + (businessDays > 0 ? " plus " : " minus ") + Math.abs(businessDays)
        + " business days: " + endDate);
Run Code Online (Sandbox Code Playgroud)

示例输出:

2015-11-26加2个工作日:2015-11-30

2015-11-26减去6个工作日:2015-11-18


ass*_*ias 7

这是一个支持正天数和负天数的版本,并将操作公开为a TemporalAdjuster.这允许你写:

LocalDate datePlus2WorkingDays = date.with(addWorkingDays(2));
Run Code Online (Sandbox Code Playgroud)

码:

/**
 * Returns the working day adjuster, which adjusts the date to the n-th following
 * working day (i.e. excluding Saturdays and Sundays).
 * <p>
 * If the argument is 0, the same date is returned if it is a working day otherwise the
 * next working day is returned.
 *
 * @param workingDays the number of working days to add to the date, may be negative
 *
 * @return the working day adjuster, not null
 */
public static TemporalAdjuster addWorkingDays(long workingDays) {
  return TemporalAdjusters.ofDateAdjuster(d -> addWorkingDays(d, workingDays));
}

private static LocalDate addWorkingDays(LocalDate startingDate, long workingDays) {
  if (workingDays == 0) return nextOrSameWorkingDay(startingDate);

  LocalDate result = startingDate;
  int step = Long.signum(workingDays); //are we going forward or backward?

  for (long i = 0; i < Math.abs(workingDays); i++) {
    result = nextWorkingDay(result, step);
  }

  return result;
}

private static LocalDate nextOrSameWorkingDay(LocalDate date) {
  return isWeekEnd(date) ? nextWorkingDay(date, 1) : date;
}

private static LocalDate nextWorkingDay(LocalDate date, int step) {
  do {
    date = date.plusDays(step);
  } while (isWeekEnd(date));
  return date;
}

private static boolean isWeekEnd(LocalDate date) {
  DayOfWeek dow = date.getDayOfWeek();
  return dow == SATURDAY || dow == SUNDAY;
}
Run Code Online (Sandbox Code Playgroud)


Jod*_*hen 5

确定工作日从根本上来说是一个循环日期的问题,检查每个日期是周末还是假日。

来自OpenGamma 的Strata项目(我是一名提交者)具有一个假期日历的实现。该API涵盖了2个工作日后查找日期的情况。该实现具有优化的位图设计,其性能优于逐日循环。这里可能很有趣。

  • 算法可以避免星期六/日,但是一旦考虑到假期,就必须循环,除非使用位图。 (3认同)