Java 9 Perlen: Datumsspielereien

Mit Java 8 wurde das neuen Date and Time API eingeführt. Als Ergänzung kommt mit Java 9 die Methode datesUntil() dazu, mit der man Datumsarithmetik machen kann. Und das wollen wir uns in dieser Folge der “Java 9 Perlen“ anschauen.

Die neue Funnktion von Java 9 ermöglicht unendliche, sequentiell angeordnete Streams:

datesUntil() – Infinite sequential ordered Stream

API

          public Stream<LocalDate> datesUntil(LocalDate endExclusive, Period step)

          Returns a sequential ordered stream of dates by given incremental step.
          The
returned stream starts from this date (inclusive) and goes to endExclusive (exclusive).

          public Stream<LocalDate> datesUntil(LocalDate endExclusive)

          This method is equivalent to datesUntil(endExclusive, Period.ofDays(1))

Oder etwas einfacher gesagt: wir bekommen einen Stream von LocalDates zwischen dem Date Objekt und dem Input Parameter endExclusive. Die Abstände zwischen den Dates können wir mittels Input Parameter step angeben. Falls wir nichts angeben, wird als step 1 Tag gewählt.

Beispiel – Hello World

Fangen wir an mit einem einfachen Beispiel. Wir möchten die Tage zwischen dem 5. und 10. Juni 2019 wissen. Ziemlich einfach mit dem neuen API.

LocalDate d1 = LocalDate.of(2019, Month.JUNE, 5);
LocalDate d2 = LocalDate.of(2019, Month.JUNE, 10);
d1.datesUntil(d2).forEach(System.out::println);

2019-06-05
2019-06-06
2019-06-07
2019-06-08
2019-06-09

Da wir einen Stream von LocalDates zurückbekommen, können wir darauf jegliche Operationen ausführen. Die folgenden Beispiel sollen einen Eindruck vermitteln, was so alles möglich ist.

Beispiel – Bis Ende Jahr

Wie viele Tage bzw. Wochen hat das aktuelle Jahr noch? Der Code für die Tage ist identisch mit obigem Beispiel. Wenn wir an Wochen interessiert sind, können wir als Period den konkreten Wert Period.ofWeeks(1) angeben.

LocalDate today = LocalDate.of(2019, Month.JUNE, 5);
LocalDate endOfYear = LocalDate.of(2020, Month.JANUARY, 1);

long remainingDays = today.datesUntil(endOfYear).count();
long remainingWeeks = today.datesUntil(endOfYear,
                                                           Period.ofWeeks(1)).count();

System.out.println(„Days until end of year: “ + remainingDays);
System.out.println(„Weeks until end of year: “ + remainingWeeks);

Days until end of year: 210
Weeks until end of year: 30

Beispiel – Freitag der 13.

Wieviel “Freitag der 13.” gibt es im aktuellen Jahr? Einfach den Stream filtern nach Wochentag und Tag. Kurz und elegant … wenn man das entsprechende API hat.

LocalDate d1 = LocalDate.of(2019, Month.JANUARY, 1);
LocalDate d2 = LocalDate.of(2020, Month.JANUARY, 1);

d1.datesUntil(d2)
        .filter(d -> d.getDayOfMonth() == 13)
        .filter(d -> d.getDayOfWeek() == DayOfWeek.FRIDAY)
        .forEach(DatesUntil::print);

2019-09-13 | FRIDAY
2019-12-13 | FRIDAY

Beispiel – Wochentage in der Zukunft

Was sind die ersten 3 Tage in der nächsten Woche? Wir starten mit einem beliebigen Datum und können mittels eines TemporalAdjusters in die Zukunft geben … zum Beispiel auf den nächsten Montag. Der Rest ist dann nur mehr ein Einschränkung des Streams auf 3 Elemente.

LocalDate today = LocalDate.of(2019, Month.JUNE, 5);
LocalDate nextMonday =
                  today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
LocalDate endOfYear = LocalDate.of(2020, Month.JANUARY, 1);

nextMonday.datesUntil(endOfYear)
        .limit(3)
        .forEach(DatesUntil::print);

2019-06-10 | MONDAY
2019-06-11 | TUESDAY
2019-06-12 | WEDNESDAY

Beispiel – Arbeitstage im nächsten Monat

Was sind die ersten 3 Arbeitstage im nächsten Monat? Mit etwas kreativem Filtering ist auch diese möglich.

LocalDate today = LocalDate.of(2019, Month.JUNE, 5);
LocalDate startNextMonth =
                  today.with(TemporalAdjusters.firstDayOfNextMonth());
LocalDate endOfYear = LocalDate.of(2020, Month.JANUARY, 1);

startNextMonth.datesUntil(endOfYear)
       .filter(d -> d.getDayOfWeek() != DayOfWeek.SATURDAY)
       .filter(d -> d.getDayOfWeek() != DayOfWeek.SUNDAY)
       .limit(3)
       .forEach(DatesUntil::print);

Beispiel – Schaltjahre

Zum Abschluss noch ein Beispiel mit Schaltjahren. Was sind die Schaltjahre der letzten 40 Jahre? Wiederum einfach zu realisieren mit den Date API, da jedes Datum selbst weiss, ob es ein Schaltjahr ist oder nicht.

LocalDate d1 = LocalDate.of(1980, Month.JANUARY, 1);
LocalDate d2 = LocalDate.of(2021, Month.JANUARY, 1);

d1.datesUntil(d2, Period.ofWeeks(1))
       .filter(d -> d.isLeapYear())
       .map(d -> d.getYear())
       .distinct()
       .forEach(System.out::println);

1980
1984
1988
1992
1996
2000
2004
2008
2012
2016
2020

Fazit

datesUntil() kommt mit Java 9 und ist eine tolle Erweiterung des Date and Time API. Man kann damit einfach und elegant komplexe Datums Abfragen machen.

Referenzen:

  • Overview DateTime
  • Temporal Adjuster

Kommentare sind geschlossen.