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

这里的其他答案指的是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) 

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

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

以下方法逐个添加天数,跳过周末,以获取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; } 

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

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

用法示例:

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

示例输出:

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

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

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

 LocalDate datePlus2WorkingDays = date.with(addWorkingDays(2)); 

码:

 /** * Returns the working day adjuster, which adjusts the date to the n-th following * working day (ie excluding Saturdays and Sundays). * 

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

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

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

这是一种向给定日历对象添加或减去工作日的方法:

 /** * This method adds workdays (MONDAY - FRIDAY) to a given calendar object. * If the number of days is negative than this method subtracts the working * days from the calendar object. * * * @param cal * @param days * @return new calendar instance */ public static Calendar addWorkDays(final Calendar baseDate, final int days) { Calendar resultDate = null; Calendar workCal = Calendar.getInstance(); workCal.setTime(baseDate.getTime()); int currentWorkDay = workCal.get(Calendar.DAY_OF_WEEK); // test if SATURDAY ? if (currentWorkDay == Calendar.SATURDAY) { // move to next FRIDAY workCal.add(Calendar.DAY_OF_MONTH, (days < 0 ? -1 : +2)); currentWorkDay = workCal.get(Calendar.DAY_OF_WEEK); } // test if SUNDAY ? if (currentWorkDay == Calendar.SUNDAY) { // move to next FRIDAY workCal.add(Calendar.DAY_OF_MONTH, (days < 0 ? -2 : +1)); currentWorkDay = workCal.get(Calendar.DAY_OF_WEEK); } // test if we are in a working week (should be so!) if (currentWorkDay >= Calendar.MONDAY && currentWorkDay <= Calendar.FRIDAY) { boolean inCurrentWeek = false; if (days > 0) inCurrentWeek = (currentWorkDay + days < 7); else inCurrentWeek = (currentWorkDay + days > 1); if (inCurrentWeek) { workCal.add(Calendar.DAY_OF_MONTH, days); resultDate = workCal; } else { int totalDays = 0; int daysInCurrentWeek = 0; // fill up current week. if (days > 0) { daysInCurrentWeek = Calendar.SATURDAY - currentWorkDay; totalDays = daysInCurrentWeek + 2; } else { daysInCurrentWeek = -(currentWorkDay - Calendar.SUNDAY); totalDays = daysInCurrentWeek - 2; } int restTotalDays = days - daysInCurrentWeek; // next working week... add 2 days for each week. int x = restTotalDays / 5; totalDays += restTotalDays + (x * 2); workCal.add(Calendar.DAY_OF_MONTH, totalDays); resultDate = workCal; } } return resultDate; } 

这是一种使用java.time类添加工作日的方法,一些function接口和lambda ……

 IntFunction addBusinessDays = days -> TemporalAdjusters.ofDateAdjuster( date -> { LocalDate baseDate = days > 0 ? date.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) : days < 0 ? date.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY)) : date; int businessDays = days + Math.min(Math.max(baseDate.until(date).getDays(), -4), 4); return baseDate.plusWeeks(businessDays / 5).plusDays(businessDays % 5); }); LocalDate.of(2018, 1, 5).with(addBusinessDays.apply(2)); //Friday Jan 5, 2018 -> Tuesday Jan 9, 2018 LocalDate.of(2018, 1, 6).with(addBusinessDays.apply(15)); //Saturday Jan 6, 2018 -> Friday Jan 26, 2018 LocalDate.of(2018, 1, 7).with(addBusinessDays.apply(-10)); //Sunday Jan 7, 2018 -> Monday Dec 25, 2017 

从任何工作日支持负值!