Rollt den Teppich wieder ein – ich komme doch nicht.

Datumsprobleme, Power Query und kein Ende.

Ich habe gestern darauf hingewiesen, dass die Altersberechnung von Power Query (es wird die Anzahl der Tage durch 365 dividiert und damit die Schalttage übergangen) sehr unscharf ist.

Man kann durch 365,25 (geschrieben: 365.25) dividieren. Das stimmt.

Oder nicht?

Nein – leider nicht ganz. Das Jahr 2024 war ein Schaltjahr, das heiß: es gab einen 29.02.2024.

Heute ist der 11.09.2024

Die Anzahl der Tage zum 11.09.2023, 11.09.2022 und 11.09.2021 betragen 366, 731 und 1096 Tage. Teilt man diese Zahlen durch 365.25 erhält man 1,0020534 beziehungsweise 2,0013689 und 3,0006845

Abgerundet also die Zahlen 1, 2 und 3

Angenommen heute wäre der 11.09.2023 (also kein Schaltjahr). Dann beträgt die Differenz zum 11.09.2022, 11.09.2021 und 11.09.2020 als Ergebnis 0,9993155 beziehungsweise 1,9986311 und 2,9979466 – oder abgerundet:

0, 1 und 2

In den Nicht-Schaltjahren bleibt ein Tag Differenz: das bedeutet: Er oder sie wird erst „einen Tag später“ ein Jahr älter.

Wir erstellen in Power Query eine einfache Funktion:

(Anfangsdatum as date, Enddatum as date) =>

    Number.RoundDown(Duration.Days(Enddatum - Anfangsdatum) / 365.25)

Randbemerkung: Leider kann man nicht Enddatum – Anfangsdatum rechnen (wie in Excel), sondern muss das Ergebnis mit Duration.Days in eine (Tages-)Zahl konvertieren.

Und so zeigt sich die Unschärfe von einem Tag:

Richtig wäre folgende Berechnung: Jahr vom Ende minus Jahr vom Anfang.

Wenn der Monat des Enddatums kleiner als der Monat des Anfangsdatums, dann muss 1 abgezogen werden.

Wenn beide Monate gleich, allerdings der Tag des Enddatums kleiner als der Tag des Anfangsdatums, dann muss 1 abgezogen werden (umgangssprachlich: er oder sie hatte noch nicht in diesem Jahr Geburtstag). Oder als Formel:

(Anfangsdatum as date, Enddatum as date) =>

    Date.Year(Enddatum) - Date.Year(Anfangsdatum) - 
    (if Date.Month(Enddatum) < Date.Month(Anfangsdatum) then 1 else 
        if Date.Month(Enddatum) = Date.Month(Anfangsdatum) and 
           Date.Day(Enddatum) < Date.Day(Anfangsdatum) then 
           1 else 0)

Rechnet korrekt:

Oder – man kann auch anders rechnen. Man transformiert das Anfangsdatum ins Jahr des Enddatums. Also: man holt Tag und Monat des Anfangsdatums und Jahr des Enddatums und baut ein Datum daraus.

Man berechnet Jahr minus Jahr.

Wenn das transformierte Datum größer als das Enddatum ist, muss noch 1 abgezogen werden.

Umgangssprachlich bei Geburtstagen: sollte er oder sie in diesem Jahr noch nicht Geburtstag gehabt haben, muss man 1 abziehen. Als Formel:

(Anfangsdatum as date, Enddatum as date) =>

    Date.Year(Enddatum) - Date.Year(Anfangsdatum) - 
    (if #date(Date.Year(Enddatum), Date.Month(Anfangsdatum), Date.Day(Anfangsdatum)) > Enddatum then
    1 else 0)

Klappt auch:

Natürlich sollte man das Enddatum optional setzen, beispielsweise so:

(Anfangsdatum as date, optional Enddatum as date) =>
    let 
        EnddatumNeu = if 
            Enddatum is null then 
            Date.From(DateTime.LocalNow()) else 
            Enddatum,
        Diff = Date.Year(EnddatumNeu) - Date.Year(Anfangsdatum),
        Alter = Diff - (if 
            #date(Date.Year(EnddatumNeu), Date.Month(Anfangsdatum), Date.Day(Anfangsdatum)) > EnddatumNeu then
            1 else 0)
    in
        Alter

Oh wie schön wäre eine Funktion DateDif oder DATEDIFF!