2015-11-26 94 views
14

這裏的其他答案是指Joda API。 我想用java.time來做。如何在向Java 8中的LocalDate添加日期的同時跳過週末?

假設今天的日期是2015年11月26日,週四,當我2個工作日添加到它, 我想要的結果,截至週一11月30日到2015年

我的工作在我自己的實現,但將是巨大的如果有東西已經存在!

編輯:

有沒有辦法做到這一點除了循環?

我試圖得到類似的功能:

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然後使用LocalDateplusDays()方法。

到目前爲止我還沒有能夠推導出它的不一致。任何人都可以確認循環,直到添加所需數量的工作日是唯一方法嗎?

回答

14

下面的方法將天一個接一個,跳過週末,爲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; 
} 

一些摸索之後,我想出了一個算法來計算數量工作日添加:

public long getActualNumberOfDaysToAdd(long workdays, int dayOfWeek) { 
    if (dayOfWeek < 6) { // date is a workday 
     return workdays + (workdays + dayOfWeek - 1)/5 * 2; 
    } else { // date is a weekend 
     return workdays + (workdays - 1)/5 * 2 + (7 - dayOfWeek); 
    } 
} 

public LocalDate add2(LocalDate date, long workdays) { 
    if (workdays < 1) { 
     return date; 
    } 

    return date.plusDays(getActualNumberOfDaysToAdd(workdays, date.getDayOfWeek().getValue())); 
} 
+0

這看起來不錯!我已經從星期一到星期天的每天添加了15個工作日,並根據需要進行了測試。你測試了多遠?我也在類似的工作,但無法通過它! –

+1

我做了一個類似的測試(在每週的每一天添加了越來越多的工作日),隨後生成了數千個隨機日期和工作日,並根據簡單循環解決方案測試結果。 –

+0

太棒了!我想知道爲什麼大多數解決方案都是指循環結束。這種解決方案在性能方面會更好,對嗎?尤其是當需要添加大量工作日時 –

4

確定工作日基本上是循環日期的問題,檢查是否ea ch是週末或假期。

來自OpenGamma的Strata項目(我是提交者)的實現爲holiday calendarAPI涵蓋了2個工作日後找到日期的情況。該實現有一個optimized bitmap design,比日復一日循環更好地執行。這裏可能很有趣。

+0

謝謝JodaStephen!你對Katja Christiansen對這個問題的解決方案有什麼意見嗎? –

+3

算法可以避免星期六/星期日,但一旦考慮假期,除非使用位圖,否則必須循環。 – JodaStephen

5

這是一個支持正數和負數天數的版本,並將操作公開爲TemporalAdjuster。這允許你寫:

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

代碼:

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

這是被增加或減少工作日給定的日曆對象的方法:

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

這是一種方法,使用java.time類添加營業日,一些功能接口& lambda ...

IntFunction<TemporalAdjuster> 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 

支持負值和任何一週!

相關問題