Datum und Zeit

Data Wrangling
R
Python
Datumsangaben einlesen, Bestandteile herausziehen, rechnen, Zeitzonen.

Kernideen

  • Solange ein Datum Text ist, sortiert es alphabetisch und lässt sich nicht verrechnen. Die Umwandlung gehört an den Import.
  • Ohne Formatangabe raten beide Sprachen, und bei Tageszahlen bis zwölf raten sie mit einer Trefferquote von 50 Prozent.
  • Für Auswertungen je Monat wird abgerundet (Monatsanfang, Periode), nicht der Monatsname gebildet: Der Name verliert das Jahr.
  • Eine Differenz ist ein eigener Typ mit Einheit, keine Zahl.
  • Monate sind verschieden lang: Ein Monat addieren und 30 Tage addieren sind zwei verschiedene Dinge.
  • Zeitzonen und Sommerzeit: Es gibt Ortszeiten, die nie stattfinden, und ein Tag hat nicht immer 24 Stunden.

Erklärung

Vorwissen: Datenimport, weil die Umwandlung dorthin gehört, und Data Wrangling für das Gruppieren nach Zeitabschnitten. Für Reihen, bei denen die Reihenfolge selbst Information trägt, siehe Zeitreihen.

Die Typen

Was R Python
Datum ohne Zeit Date datetime64[ns], Timestamp
Zeitpunkt POSIXct Timestamp mit tz
Differenz difftime Timedelta
Kalenderabschnitt floor_date() auf Monat, Woche Period, to_period("M")

Einlesen mit Formatangabe

In R heissen die Parser nach der Reihenfolge der Bestandteile: ymd(), dmy(), mdy(), für Zeitstempel ymd_hms(). In Python nimmt pd.to_datetime() ein format in der Schreibweise mit Prozentzeichen (%d.%m.%Y). Ohne Angabe raten beide. Wer das Format kennt, gibt es an; wer es nicht kennt, sieht sich die Rohwerte an, bevor er importiert.

Zeichen Bedeutung Beispiel
%d Tag zweistellig 05
%m Monat zweistellig 03
%Y Jahr vierstellig 2026
%y Jahr zweistellig 26
%H:%M:%S Stunde, Minute, Sekunde 14:30:00

Gruppieren nach Zeitabschnitten

Für eine Auswertung je Monat gibt es zwei Wege, und nur einer ist richtig:

Weg Ergebnis tauglich
Monatsname oder %m als Text “01”, “Januar” nein, das Jahr fehlt
auf den Monatsanfang abrunden 2026-01-01 ja
Periode 2026-01 ja

Dasselbe gilt für Wochen. Dort kommt hinzu, dass die ISO-Woche zum Jahreswechsel zu einem anderen Jahr gehören kann als das Datum: Der 1. Januar 2027 liegt in der ISO-Woche 53 des Jahres 2026. Wer nur nach der Wochennummer gruppiert, wirft zwei Jahre zusammen; richtig ist das Paar aus ISO-Jahr und ISO-Woche.

Rechnen

+ days(30) addiert 30 Tage, %m+% months(1) beziehungsweise DateOffset(months=1) addiert einen Monat, und ein Monat ist je nach Monat 28 bis 31 Tage lang. Beim Monatsende kommt hinzu, dass es den Zieltag nicht geben muss: der 31. Januar plus ein Monat. R liefert mit dem gewöhnlichen + months(1) dafür NA, mit %m+% den letzten Tag des Zielmonats; pandas nimmt mit DateOffset ebenfalls den letzten Tag.

Zeitzonen

Operation R Python Wirkung
umwandeln with_tz() tz_convert() derselbe Moment, andere Darstellung
festlegen force_tz() tz_localize() anderer Moment, gleiche Ziffern

Zwei Regeln vermeiden die meisten Probleme: intern in UTC speichern und rechnen, erst für die Ausgabe umwandeln. Die Sommerzeit macht zusätzlich zwei Dinge kaputt: Im Frühling gibt es eine Ortszeit, die nicht existiert, im Herbst eine, die zweimal vorkommt.

Kurz nachgeschlagen

Aufgabe R (lubridate) Python (pandas)
Text zu Datum dmy(x), ymd_hms(x) pd.to_datetime(x, format="%d.%m.%Y")
beim Import col_date(format = "%d.%m.%Y") parse_dates=["datum"], dayfirst=True
Bestandteile year(), month(), day(), wday() .dt.year, .dt.month, .dt.day_name()
auf Monat runden floor_date(d, "month") .dt.to_period("M")
ISO-Woche isoweek(), isoyear() .dt.isocalendar().week und .year
Differenz in Tagen as.numeric(b - a, units = "days") (b - a).days
Monat addieren d %m+% months(1) d + pd.DateOffset(months=1)
Zeitachse erzeugen seq(von, bis, by = "day") pd.date_range(von, bis, freq="D")
Zone umwandeln with_tz(t, "Europe/Zurich") t.tz_convert("Europe/Zurich")

Beispiele

Frage und Datenlage

Drei Datumsangaben als Text: 05.01.2026, 10.01.2026, 02.02.2026. Wie sortieren sie als Text, wie als Datum? Und was macht 03.04.2026 aus einem Import ohne Formatangabe?

Rechnung

roh <- c("05.01.2026", "10.01.2026", "02.02.2026")
sort(roh)                       # alphabetisch, also falsch
[1] "02.02.2026" "05.01.2026" "10.01.2026"
datum <- dmy(roh)
sort(datum)
[1] "2026-01-05" "2026-01-10" "2026-02-02"
class(datum)
[1] "Date"
c(als_dmy = format(dmy("03.04.2026")), als_mdy = format(mdy("03.04.2026")))
     als_dmy      als_mdy 
"2026-04-03" "2026-03-04" 
roh = pd.Series(["05.01.2026", "10.01.2026", "02.02.2026"])
print(roh.sort_values().tolist())        # alphabetisch, also falsch
['02.02.2026', '05.01.2026', '10.01.2026']
datum = pd.to_datetime(roh, format="%d.%m.%Y")
print(datum.sort_values().dt.strftime("%d.%m.%Y").tolist())
['05.01.2026', '10.01.2026', '02.02.2026']
print(datum.dtype)
datetime64[ns]
print(pd.to_datetime("03.04.2026", format="%d.%m.%Y").strftime("%Y-%m-%d"),
      pd.to_datetime("03.04.2026", format="%m.%d.%Y").strftime("%Y-%m-%d"))
2026-04-03 2026-03-04

Output Zeile für Zeile

Schritt Ergebnis Erklärung
als Text sortiert 02.02., 05.01., 10.01. Der 2. Februar steht vorn, weil die Ziffer 0 vor der 1 kommt. Verglichen wird zeichenweise.
als Datum sortiert 05.01., 10.01., 02.02. richtige Reihenfolge
Typ Date beziehungsweise datetime64[ns] Erst damit sind Differenzen und Gruppierungen möglich.
03.04.2026 als Tag zuerst 2026-04-03 der 3. April
dieselbe Zeichenkette als Monat zuerst 2026-03-04 der 4. März

Interpretation und Ergebnissatz

Dieselbe Zeichenkette ergibt zwei verschiedene Daten, und beide Lesarten sind gültig. Ein Import, der rät, entscheidet das oft anhand der Spracheinstellung des Rechners; dieselbe Datei wird dann auf zwei Rechnern verschieden eingelesen. Auffallen wird es frühestens bei einer Auswertung nach Monat, und dann fehlt der Zusammenhang zur Ursache.

Als Text sortiert steht der 2. Februar vor dem 5. Januar. Die Zeichenkette 03.04.2026 ergibt je nach Lesart den 3. April oder den 4. März, weshalb das Format immer anzugeben ist.

Frage und Datenlage

Vom 15. März 2026 sollen Jahr, Monat, Tag, Wochentag und ISO-Woche bestimmt werden. Danach vier Werte aus Januar 2025, Januar 2026 und Februar 2026: Was passiert, wenn nach dem Monat als Text gruppiert wird?

Rechnung

d <- ymd("2026-03-15")
c(jahr = year(d), monat = month(d), tag = day(d),
  wochentag = wday(d, week_start = 1), woche = isoweek(d))
     jahr     monat       tag wochentag     woche 
     2026         3        15         7        11 
floor_date(d, "month")
[1] "2026-03-01"
ueber <- data.frame(
  datum = ymd(c("2025-01-15", "2025-01-28", "2026-01-12", "2026-02-03")),
  wert  = c(10, 20, 30, 40))

tapply(ueber$wert, format(ueber$datum, "%m"), sum)          # nur Monat
01 02 
60 40 
tapply(ueber$wert, floor_date(ueber$datum, "month"), sum)   # Monatsanfang
2025-01-01 2026-01-01 2026-02-01 
        30         30         40 
d = pd.Timestamp("2026-03-15")
print(d.year, d.month, d.day, d.day_name(), d.isocalendar().week)
2026 3 15 Sunday 11
print(d.to_period("M").start_time.date())
2026-03-01
ueber = pd.DataFrame({
    "datum": pd.to_datetime(["2025-01-15", "2025-01-28", "2026-01-12", "2026-02-03"]),
    "wert": [10, 20, 30, 40]})

print(ueber.groupby(ueber["datum"].dt.strftime("%m"))["wert"].sum().to_dict())
{'01': 60, '02': 40}
print({str(k): v for k, v in
       ueber.groupby(ueber["datum"].dt.to_period("M"))["wert"].sum().items()})
{'2025-01': 30, '2026-01': 30, '2026-02': 40}

Output Zeile für Zeile

Grösse Wert Erklärung
Jahr, Monat, Tag 2026, 3, 15
Wochentag 7 beziehungsweise Sunday Mit week_start = 1 zählt R ab Montag, die 7 ist der Sonntag.
ISO-Woche 11
Monatsanfang 2026-03-01 floor_date beziehungsweise to_period("M")
nach Monat als Text {'01': 60, '02': 40} Falsch: Die 60 fasst Januar 2025 (10 + 20) und Januar 2026 (30) zusammen. Zwei Jahre in einer Gruppe.
nach Monatsanfang 2025-01: 30, 2026-01: 30, 2026-02: 40 Jetzt trennt die Gruppierung die Jahre.

Interpretation und Ergebnissatz

Der Monatsname ist eine Beschriftung, kein Zeitabschnitt. Er verliert das Jahr, und das fällt nur auf, wenn man die Summen nachrechnet, denn die Tabelle sieht in beiden Fällen gleich plausibel aus. Für eine Grafik ist der abgerundete Monatsanfang zusätzlich praktisch, weil er auf einer Zeitachse richtig platziert wird, während Text alphabetisch einsortiert würde.

Mit dem Monat als Text entsteht eine Januargruppe mit 60, die zwei Jahre vermischt; nach Monatsanfang gruppiert stehen Januar 2025 und Januar 2026 getrennt bei je 30.

Frage und Datenlage

Vom 5. Januar bis zum 15. März 2026: Wie gross ist die Differenz? Und was ergibt der 31. Januar plus ein Monat, verglichen mit plus 31 Tagen?

Rechnung

start <- ymd("2026-01-05")
ende  <- ymd("2026-03-15")

ende - start
Time difference of 69 days
as.numeric(ende - start, units = "days")
[1] 69
start + days(30)
[1] "2026-02-04"
start %m+% months(1)
[1] "2026-02-05"
ende_jan <- ymd("2026-01-31")
ende_jan + months(1)        # Zieltag gibt es nicht
[1] NA
ende_jan %m+% months(1)     # auf den letzten Tag gesetzt
[1] "2026-02-28"
ende_jan + days(31)
[1] "2026-03-03"
start, ende = pd.Timestamp("2026-01-05"), pd.Timestamp("2026-03-15")

print(ende - start, "|", (ende - start).days)
69 days 00:00:00 | 69
print((start + pd.Timedelta(days=30)).date())
2026-02-04
print((start + pd.DateOffset(months=1)).date())
2026-02-05
ende_jan = pd.Timestamp("2026-01-31")
print((ende_jan + pd.DateOffset(months=1)).date())
2026-02-28
print((ende_jan + pd.Timedelta(days=31)).date())
2026-03-03

Output Zeile für Zeile

Rechnung Ergebnis Erklärung
ende - start 69 Tage, als eigener Typ R zeigt Time difference of 69 days, Python 69 days 00:00:00. Keine blosse Zahl.
in Tagen 69 Erst die ausdrückliche Umwandlung macht eine Zahl daraus.
5. Januar plus 30 Tage 2026-02-04
5. Januar plus 1 Monat 2026-02-05 Ein Tag später als plus 30 Tage, weil der Januar 31 Tage hat.
31. Januar plus 1 Monat, R mit + months(1) NA Den 31. Februar gibt es nicht. R sagt das deutlich.
31. Januar plus 1 Monat, %m+% / DateOffset 2026-02-28 auf den letzten Tag des Zielmonats gesetzt
31. Januar plus 31 Tage 2026-03-03 Drei Tage weiter als “ein Monat später”.

Interpretation und Ergebnissatz

“Ein Monat” ist keine Dauer, sondern eine Kalenderoperation. Wer Fristen rechnet (Kündigung, Garantie, Folgetermin), muss sich entscheiden, was gemeint ist, und es aufschreiben. Das NA in R ist dabei die ehrlichere Voreinstellung als das stille Verschieben auf den Monatsletzten, weil es zur Entscheidung zwingt.

Zwischen dem 5. Januar und dem 15. März 2026 liegen 69 Tage. Der 31. Januar plus ein Monat ergibt den 28. Februar (in R mit + months(1) ein NA), plus 31 Tage dagegen den 3. März.

Frage und Datenlage

In der Nacht zum 29. März 2026 wird in Europa auf Sommerzeit umgestellt. Was zeigt ein UTC-Zeitstempel von 00:30 und einer von 01:30 in Zürich? Was passiert mit der Ortszeit 02:30, und wie lang ist die Spanne von 00:30 bis 03:30 Ortszeit?

Rechnung

t1 <- ymd_hms("2026-03-29 00:30:00", tz = "UTC")
t2 <- ymd_hms("2026-03-29 01:30:00", tz = "UTC")

with_tz(t1, "Europe/Zurich")
[1] "2026-03-29 01:30:00 CET"
with_tz(t2, "Europe/Zurich")
[1] "2026-03-29 03:30:00 CEST"
force_tz(ymd_hms("2026-03-29 02:30:00"), "Europe/Zurich")   # gibt es nicht
[1] NA
a <- ymd_hms("2026-03-29 00:30:00", tz = "Europe/Zurich")
b <- ymd_hms("2026-03-29 03:30:00", tz = "Europe/Zurich")
b - a
Time difference of 2 hours
t1 = pd.Timestamp("2026-03-29 00:30:00", tz="UTC")
t2 = pd.Timestamp("2026-03-29 01:30:00", tz="UTC")

print(t1.tz_convert("Europe/Zurich"))
2026-03-29 01:30:00+01:00
print(t2.tz_convert("Europe/Zurich"))
2026-03-29 03:30:00+02:00
try:
    pd.Timestamp("2026-03-29 02:30:00").tz_localize("Europe/Zurich")
except Exception as fehler:
    print(type(fehler).__name__)
NonExistentTimeError
a = pd.Timestamp("2026-03-29 00:30", tz="Europe/Zurich")
b = pd.Timestamp("2026-03-29 03:30", tz="Europe/Zurich")
print(b - a)
0 days 02:00:00

Output Zeile für Zeile

Zeitpunkt in Zürich Erklärung
00:30 UTC 01:30 +01:00 (MEZ) vor der Umstellung
01:30 UTC 03:30 +02:00 (MESZ) Eine Stunde UTC später, aber zwei Stunden auf der Ortsuhr: Um 02:00 sprang sie auf 03:00.
Ortszeit 02:30 festlegen NonExistentTimeError beziehungsweise NA Diese Ortszeit hat es nie gegeben. Beide Sprachen verweigern die Angabe.
00:30 bis 03:30 Ortszeit 2 Stunden Auf der Uhr sind es drei, tatsächlich zwei.

Interpretation und Ergebnissatz

Die letzte Zeile ist der Punkt, an dem Auswertungen still falsch werden: Wer Dauern aus Ortszeiten berechnet, bekommt zweimal im Jahr eine Stunde geschenkt oder gestohlen. Deshalb die Regel, intern in UTC zu rechnen und erst zur Anzeige umzuwandeln. Und deshalb ist es ein gutes Zeichen, wenn eine Bibliothek bei der nicht existierenden Ortszeit abbricht, statt etwas anzunehmen.

Ein UTC-Zeitstempel von 01:30 ist in Zürich bereits 03:30, während 00:30 UTC erst 01:30 ist. Die Ortszeit 02:30 gibt es an diesem Tag nicht, und zwischen 00:30 und 03:30 Ortszeit liegen nur zwei Stunden.

Frage und Datenlage

Drei Tage um den Jahreswechsel: 28. Dezember 2026, 31. Dezember 2026 und 1. Januar 2027. In welcher ISO-Woche liegen sie, und was passiert beim Gruppieren nach der Wochennummer?

Rechnung

tage <- ymd(c("2026-01-01", "2026-12-28", "2026-12-31", "2027-01-01"))
data.frame(tag = tage, wochentag = wday(tage, week_start = 1),
           iso_jahr = isoyear(tage), iso_woche = isoweek(tage))
         tag wochentag iso_jahr iso_woche
1 2026-01-01         4     2026         1
2 2026-12-28         1     2026        53
3 2026-12-31         4     2026        53
4 2027-01-01         5     2026        53
reihe <- data.frame(datum = ymd(c("2026-12-28", "2026-12-31", "2027-01-01")),
                    wert  = c(1, 2, 3))

tapply(reihe$wert, isoweek(reihe$datum), sum)          # nur Woche
53 
 6 
reihe |>
  mutate(iso_jahr = isoyear(datum), iso_woche = isoweek(datum)) |>
  summarise(.by = c(iso_jahr, iso_woche), summe = sum(wert))
  iso_jahr iso_woche summe
1     2026        53     6
tage = pd.to_datetime(["2026-01-01", "2026-12-28", "2026-12-31", "2027-01-01"])
iso = tage.isocalendar()
print(pd.DataFrame({"tag": tage.strftime("%Y-%m-%d"),
                    "wochentag": tage.day_name().str[:3],
                    "iso_jahr": iso["year"].values,
                    "iso_woche": iso["week"].values}).to_string(index=False))
       tag wochentag  iso_jahr  iso_woche
2026-01-01       Thu      2026          1
2026-12-28       Mon      2026         53
2026-12-31       Thu      2026         53
2027-01-01       Fri      2026         53
reihe = pd.DataFrame({"datum": pd.to_datetime(["2026-12-28", "2026-12-31", "2027-01-01"]),
                      "wert": [1, 2, 3]})
iso_r = reihe["datum"].dt.isocalendar()
print(reihe.groupby(iso_r["week"])["wert"].sum().to_dict())
{np.uint32(53): 6}
print({f"{j}-W{w}": v for (j, w), v in
       reihe.groupby([iso_r["year"], iso_r["week"]])["wert"].sum().items()})
{'2026-W53': 6}

Output Zeile für Zeile

Tag Wochentag ISO-Jahr ISO-Woche Erklärung
2026-01-01 Donnerstag 2026 1 Die ISO-Woche 1 ist die Woche mit dem ersten Donnerstag des Jahres.
2026-12-28 Montag 2026 53 Das Jahr 2026 hat 53 ISO-Wochen.
2026-12-31 Donnerstag 2026 53
2027-01-01 Freitag 2026 53 Der 1. Januar 2027 gehört zur letzten Woche des ISO-Jahres 2026.
Gruppierung Ergebnis Erklärung
nur nach Woche Woche 53: 6 Alle drei Werte in einer Gruppe. Hier zufällig richtig, weil alle drei Tage wirklich in derselben ISO-Woche liegen.
nach ISO-Jahr und Woche (2026, 53): 6 Dieselbe Gruppe, jetzt aber eindeutig benannt. Kämen Tage aus der Woche 53 eines anderen Jahres dazu, träfe nur diese Form die Unterscheidung.

Interpretation und Ergebnissatz

Die Wochennummer allein ist kein Schlüssel. Über mehrere Jahre hinweg gruppiert sie Woche 53 aus 2020 und 2026 zusammen, und am Jahreswechsel gehören Tage zu einem ISO-Jahr, das nicht ihr Kalenderjahr ist. Wer nach Wochen auswertet, nimmt deshalb immer das Paar aus ISO-Jahr und ISO-Woche, oder rundet gleich auf den Wochenanfang ab.

Der 1. Januar 2027 liegt in der ISO-Woche 53 des Jahres 2026, zusammen mit dem 28. und dem 31. Dezember 2026; ihre Summe beträgt 6. Nur das Paar aus ISO-Jahr und Woche benennt diese Gruppe eindeutig.

Verständnisfragen

Ein Datensatz enthält die Spalte datum als Text im Format 03.04.2026, und der Import hat sie automatisch umgewandelt. Warum ist das ein Risiko?

Kein Risiko, die Erkennung ist zuverlässig
Sie ist es genau dann nicht, wenn beide Lesarten möglich sind.
Bei Tageszahlen bis zwölf sind Tag und Monat vertauschbar
Richtig. 03.04.2026 kann der 3. April oder der 4. März sein, wie Beispiel 1 zeigt. Die Erkennung wählt eine Lesart, oft je nach Spracheinstellung.
Weil Text grundsätzlich nicht umgewandelt werden darf
Umgewandelt gehört er unbedingt, nur mit ausdrücklichem Format.

Eine Auswertung gruppiert Umsätze nach dem Monatsnamen, den sie aus dem Datum gebildet hat. Was geht dabei schief?

Gleiche Monate verschiedener Jahre landen in derselben Gruppe
Richtig, wie die Januarsumme 60 in Beispiel 2. Richtig ist, auf den Monatsanfang abzurunden oder Jahr und Monat gemeinsam zu gruppieren.
Die Sortierung wird alphabetisch
Das passiert zusätzlich, ist aber nur ein Darstellungsproblem.
Nichts, das ist der übliche Weg
Er ist üblich und trotzdem falsch, sobald mehr als ein Jahr in den Daten steckt.

Eine Frist läuft “einen Monat” ab dem 31. Januar. Welches Ergebnis ist zu erwarten?

Der 28. Februar, sofern die Bibliothek auf den Monatsletzten setzt
Richtig, mit %m+% beziehungsweise DateOffset. R liefert mit dem gewöhnlichen + months(1) stattdessen NA, weil es den 31. Februar nicht gibt.
Der 3. März, also 31 Tage später
Das wäre “plus 31 Tage”, eine andere Rechnung.
Ein Fehler, weil Monate keine feste Länge haben
Genau deshalb gibt es eigene Operatoren dafür.

Eine Auswertung berechnet Arbeitsdauern als Differenz zweier Ortszeiten. In der Nacht der Zeitumstellung ist eine Schicht um eine Stunde zu lang. Was ist die Ursache?

Zwischen 00:30 und 03:30 Ortszeit liegen an diesem Tag nur zwei Stunden
Richtig. Die Ortsuhr sprang von 02:00 auf 03:00. Wer in UTC rechnet und erst zur Anzeige umwandelt, hat das Problem nicht.
Die Zeitzone war falsch eingestellt
Auch mit richtiger Zone gilt der Sprung.
Es ist ein Rundungsfehler
Es geht um eine ganze Stunde.

Eine Wochenauswertung über drei Jahre gruppiert nach isoweek() beziehungsweise .dt.isocalendar().week. Was fehlt?

Das ISO-Jahr; sonst fällt Woche 53 aus mehreren Jahren zusammen
Richtig. Dazu kommt, dass der 1. Januar 2027 zur Woche 53 des ISO-Jahres 2026 gehört, wie Beispiel 5 zeigt.
Nichts, die Wochennummer ist eindeutig
Sie wiederholt sich jedes Jahr.
Der Wochentag
Für eine Wochengruppierung ist er nicht nötig.

Verlinkte Ressourcen