2015-11-26 5 views
14

Andere Antworten beziehen sich hier auf Joda API. Ich möchte es mit java.time tun.Wie überspringe ich Wochenenden, während ich Tage zu LocalDate in Java 8 hinzufüge?

Angenommen, die heutige Datum 26. November 2015 bis Donnerstag, als ich 2 Werktag in dem es, ich das Ergebnis am Montag 30. November will 2015

Ich arbeite an meiner eigenen Implementierung, aber es wäre toll wenn etwas schon existiert!

EDIT:

Gibt es eine Möglichkeit, es auseinander zu tun von Schleifen über?

Ich habe versucht, eine Funktion wie abzuleiten:

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) 

Dann X1 und X2 gegeben (ab dem ersten Tag der Woche des Datums abgeleitet), wir Y und verwenden Sie dann plusDays() Methode von LocalDate finden.

Ich konnte es bisher nicht ableiten, es ist nicht konsistent. Kann jemand bestätigen, dass das Loop-Over bis zur gewünschten Anzahl von Arbeitstagen der einzige Weg ist?

Antwort

14

Die folgende Methode Tage nacheinander hinzufügt, das Überspringen Wochenenden, für positive Werte von 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; 
} 

Nach einigen Hantieren, kam ich mit einem Algorithmus zu berechnen bis die Anzahl der hinzuzufügende Arbeitstage:

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

Das sieht gut aus!Ich habe es getestet, indem ich bis zu 15 Arbeitstage pro Tag von Montag bis Sonntag hinzugefügt habe und es bei Bedarf funktioniert. Wie weit hast du es getestet? Ich arbeitete auch an ähnlichen Linien, konnte aber nicht durchkommen! –

+1

Ich führte einen ähnlichen Test durch (es wurden immer mehr Arbeitstage an jedem Wochentag hinzugefügt), gefolgt von der Generierung tausender zufälliger Daten und Arbeitstage und dem Testen der Ergebnisse gegen die einfache Loop-Lösung. –

+0

Großartig! Ich frage mich, warum sich die meisten Lösungen auf das Umschlingen beziehen. Diese Lösungen wären in Bezug auf die Leistung viel besser, oder? Vor allem, wenn eine große Anzahl von Arbeitstagen hinzugefügt werden muss. –

4

Bestimmung von Arbeitstagen ist grundlegend eine Frage des Durchschleifens von Daten und prüft, ob ea ch ist ein Wochenende oder Urlaub.

Das Strata Projekt von OpenGamma (ich bin ein Committer) hat eine Implementierung von holiday calendar. Die API deckt den Fall der Suche nach dem Datum 2 Werktage später. Die Implementierung verfügt über eine optimized bitmap design, die eine bessere Leistung bietet als eine tägliche Schleife. Es könnte hier von Interesse sein.

+0

Danke JodaStephen! Haben Sie Anmerkungen zu Katja Christiansens Lösung dieser Frage? –

+3

Algorithmen können arbeiten, um Sat/Sun zu vermeiden, aber wenn Sie Feiertage berücksichtigen, müssen Sie eine Schleife erstellen, sofern Sie keine Bitmaps verwenden. – JodaStephen

5

Hier ist eine Version, die sowohl positive als auch negative Anzahl von Tagen unterstützt und den Vorgang als TemporalAdjuster darstellt. Das ermöglicht es Ihnen, schreiben:

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

Code:

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

Dies ist eine Methode, die das Hinzufügen oder Werktage zu einem bestimmten Kalenderobjekt subtrahieren:

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

Dies ist eine Möglichkeit, Hinzufügen von Geschäftstagen mit Hilfe von java.time Klassen, einige funktionale Schnittstellen & 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 

Unterstützt negative Werte und von jedem Wochentag!

Verwandte Themen