# invisible
import numpy as np
np.core.arrayprint._line_width = 65

Python, Datum und Zeit

Python Standard-Module für Zeit-Daten

Python, Date and Time

Python bietet reichlich Funktionalität, um mit Datums- und Zeit-Daten umzugehen. Die Standard-Bibliotheken enthalten folgende Module:

  • time
  • calendar
  • datetime

Diese Module bieten Klassen zur Manipulation von simplen und komplexen Datums- und Zeit-Daten.

Speziell die datetime-Klasse ist sehr wichtig für die Timeseries in Pandas.

Die wichtigsten Module in Python, um mit Zeiten zu arbeiten, sind time, calendar und datetime.

Das datetime-Modul bietet diverse Klassen, Methoden und Funktionen für die Arbeit mit Daten, Zeiten und Zeit-Intervallen. Dafür stehen folgende Klassen zur Verfügung:

  • Die Instanzen der date-Klasse können Daten innerhalb der Jahre zwischen 1 und 9999 abbilden.
  • Eine Instanz der datetime-Klasse besteht sowohl aus dem Datum, als auch aus der Zeit.
  • Die time-Klasse implementiert Zeit-Objekte.
  • Die timedelta-Klasse wird verwendet zur Differenzbildung zwischen zwei Zeit- oder Datums-Objekten.
  • Die tzinfo-Klasse dient der Implementierung von Zeitzonen für Zeit- und Datums-Objekten.

Wir starten mit dem date-Objekt.

Die date-Klasse

from datetime import date
x = date(1993, 12, 14)
print(x)
1993-12-14

Wir können Datums-Objekte zwischen dem 01. Januar 0001 und dem 31. Dezember 9999 instanziieren. Über die Attribute min und max kann dies ermittelt werden:

from datetime import date
print(date.min)
print(date.max)
0001-01-01
9999-12-31

Wir können diverse Methoden auf das obige date-Objekt anwenden. Das proleptische Gregorianische Ordinal liefert die Methode toordinal. Der proleptische gregorianische Kalender besteht aus der Erweiterung des gregorianischen Kalenders rückwärts über seine Einführung im Jahre 1582 hinaus. In der Ordinalen Numerierung entspricht somit dem Tag 1 der 1. Januar des Jahres 1:

x = date(1, 1, 1) # 1. Januar 1
print(x.toordinal())
x = date(1, 1, 2) # 2. Januar 1
print(x.toordinal())
print(x.today())
print(x.today().toordinal())
1
2
2019-02-21
737111

Aus einem Ordinal kann das Datum mit der Klassen-Methode fromordinal wieder herausgerechnet werden:

print(date.fromordinal(726952))
1991-04-30

Wenn Sie den Wochentag eines bestimmten Tages wissen möchten, kann dies mit der Methode weekday berechnet werden. weekday liefert Zahlen zwischen 0 (Montag) und 6 (Sonntag) zurück.

print(x.weekday())
1
print(date.today())
2019-02-21

Über die Attribute können wir auf den Tag, den Monat und das Jahr eines Date-Objektes zugreifen:

print(x.day)
print(x.month)
print(x.year)
2
1
1

Die time-Klasse

Die time-Klasse ist gleich organisiert wie die date-Klasse.

from datetime import time
t = time(15, 6, 23)
print(t)
15:06:23

Die möglichen Zeiten liegen zwischen:

print(time.min)
print(time.max)
00:00:00
23:59:59.999999

Der Zugriff auf Stunde, Minute und Sekunde:

print(t.hour, t.minute, t.second)
15 6 23

Jede Komponente eines Zeit-Objektes kann durch replace() geändert werden:

t = t.replace(hour=11, minute=59)
print(t)
11:59:23

Wir können einen Datums-String in C-Style generieren, entsprechend der ctime-Funktion in C:

print(x.ctime())
Tue Jan  2 00:00:00 0001

Die datetime-Klasse

Das datetime-Modul bietet uns Funktionen und Methoden zur Manipulation von Datums- und Zeit-Objekten. Weiterhin stellt es Funktionalitäten zur Verfügung für arithmetische Operationen für Datums- und Zeit-Objekte, z.B. Addition und Subtraktion. Ein weiterer Fokus der Implementierung liegt auf der Extrahierung von Attributen.

Es gibt zwei Arten von Datums- und Zeit-Objekten:

  • naive
  • aware

Wenn ein Zeit-Objekt 'naive' ist, enthält es keine Informationen für den Vergleich oder die Lokalisation gegenüber anderen Datums- oder Zeit-Objekten. Die Semantik, falls das 'naive'-Objekt einer bestimmten Zeitzone entspricht (wie beispielsweise UTC, lokale Zeit, etc.), ist in der Logik des Programs verankert.

Auf der anderen Seite hat ein 'aware'-Objekt Informationen über die Zeitzone. Somit kann es gegenüber anderen 'aware'-Objekten lokalisiert werden.

Wie können Sie herausfinden, ob ein datetime-Objekt t 'aware' ist?

t ist 'aware', wenn t.tzinfo nicht None ist und t.tzinfo.utcoffset(t) nicht None ist. Beide Bedingungen müssen erfüllt sein.

Demgegenüber ist das Objekt t 'naive', wenn t.tzinfo oder t.tzinfo.utcoffset(t) None ist.

Erstellen wir ein datetime-Objekt:

from datetime import datetime
t = datetime(2017, 4, 19, 16, 31, 0)
print(t)
2017-04-19 16:31:00

t ist naive, weil folgender Ausdruck True ist:

print(t.tzinfo == None)
True

Wir erstellen ein 'aware' datetime-Objekt vom aktuellen Datum. Dafür benötigen wir das Modul pytz. pytz ist ein Modul, welches die 'Olsen-Zeitzonen-Datenbank' in Python bereitstellt. Die Olsen-Zeitzonen werden nahezu komplett durch dieses Modul unterstützt.

from datetime import datetime
import pytz
t = datetime.now(pytz.utc)

Wir sehen, dass sowohl t.tzinfo als auch t.tzinfo.utcoffset(t) nicht None sind und t somit ein 'aware'-Objekt ist:

print(t.tzinfo, t.tzinfo.utcoffset(t))
UTC 0:00:00
from datetime import datetime, timedelta as delta
ndays = 15
start = datetime(1991, 4, 30)
dates = [start - delta(days=x) for x in range(0, ndays)]
dates
Ausgabe: :

[datetime.datetime(1991, 4, 30, 0, 0),
 datetime.datetime(1991, 4, 29, 0, 0),
 datetime.datetime(1991, 4, 28, 0, 0),
 datetime.datetime(1991, 4, 27, 0, 0),
 datetime.datetime(1991, 4, 26, 0, 0),
 datetime.datetime(1991, 4, 25, 0, 0),
 datetime.datetime(1991, 4, 24, 0, 0),
 datetime.datetime(1991, 4, 23, 0, 0),
 datetime.datetime(1991, 4, 22, 0, 0),
 datetime.datetime(1991, 4, 21, 0, 0),
 datetime.datetime(1991, 4, 20, 0, 0),
 datetime.datetime(1991, 4, 19, 0, 0),
 datetime.datetime(1991, 4, 18, 0, 0),
 datetime.datetime(1991, 4, 17, 0, 0),
 datetime.datetime(1991, 4, 16, 0, 0)]

Unterschied zwischen Zeiten

Schauen wir was passiert, wenn wir datetime-Objekte voneinander subtrahieren:

from datetime import datetime
delta = datetime(1993, 12, 14) - datetime(1991, 4, 30)
print(delta, type(delta))
959 days, 0:00:00 <class 'datetime.timedelta'>

Das Ergebnis der Subtraktion der beiden datetime-Objekte ist ein timedelta-Objekt. Über das Attribut days können wir die Tage der Differenz auslesen:

print(delta.days)
959
t1 = datetime(2017, 1, 31, 14, 17)
t2 = datetime(2015, 12, 15, 16, 59)
delta = t1 - t2
print(delta.days, delta.seconds)
412 76680

Es ist möglich, ein timedelta-Objekt von einem anderen datetime-Objekt zu subtrahieren oder es zu addieren (in Tagen), um ein neues datetime-Objekt zu berechnen:

from datetime import datetime, timedelta
d1 = datetime(1991, 4, 30)
d2 = d1 + timedelta(10)
print(d2)
print(d2 - d1)
d3 = d1 - timedelta(100)
print(d3)
d4 = d1 - 2 * timedelta(50)
print(d4)
1991-05-10 00:00:00
10 days, 0:00:00
1991-01-20 00:00:00
1991-01-20 00:00:00

Ebenso können timedelta-Objekte auch in Tagen und Minuten zu datetime-Objekten addiert oder voneinander subtrahiert werden:

from datetime import datetime, timedelta
d1 = datetime(1991, 4, 30)
d2 = d1 + timedelta(10,100)
print(d2)
print(d2 - d1)
1991-05-10 00:01:40
10 days, 0:01:40

Wandlung von datetime-Objekten in Strings

Der einfachste Weg ein datetime-Objekt als String darzustellen ist die str-Methode.

s = str(d1)
print(s)
1991-04-30 00:00:00

Wandlung mit strftime

Der Methodenaufruf datetime.strftime(format) liefert einen String zurück, welcher die Zeit und das Datum repräsentiert, jedoch durch ein explizites Format bestimmt wird. Ein komplette Liste von möglichen Formatierungen kann hier (strftime) eingesehen werden:

print(d1.strftime('%Y-%m-%d'))
print("Wochentag: " + d1.strftime('%a'))
print("Wochentag ausgeschrieben: " + d1.strftime('%A'))
# Weekday as a decimal number, where 0 is Sunday 
# and 6 is Saturday
print("Wochentag als Dezimalzahl: " + d1.strftime('%w'))
1991-04-30
Wochentag: Tue
Wochentag ausgeschrieben: Tuesday
Wochentag als Dezimalzahl: 2

Formatierung von Monaten:

# Day of the month as a zero-padded decimal number. 
# 01, 02, ..., 31
print(d1.strftime('%d'))
# Month as locale’s abbreviated name. 
# Jan, Feb, ..., Dec (en_US); 
# Jan, Feb, ..., Dez (de_DE)
print(d1.strftime('%b'))
# Month as locale’s full name. 	
# January, February, ..., December (en_US);
# Januar, Februar, ..., Dezember (de_DE)
print(d1.strftime('%B'))
# Month as a zero-padded decimal number. 
# 01, 02, ..., 12
print(d1.strftime('%m'))
30
Apr
April
04

Ausgabe in Landessprache

Wir haben bereits gesehen, dass die Datumsausgaben in Englisch erfolgt sind. Im Folgenden geben wir ein Datum auf die im Vereinigten Königreich gebräuchlichste Art aus:

from datetime import datetime, timedelta
d1 = datetime(1993, 12, 14)
print(d1.strftime('%d %B %Y'))
print("Nur in Zahlen:")
print(d1.strftime('%d/%m/%Y'))
print("Auf US Art:")
print(d1.strftime('%m/%d/%Y'))
print(f"It was a {d1.strftime('%A'):s}!")
14 December 1993
Nur in Zahlen:
14/12/1993
Auf US Art:
12/14/1993
It was a Tuesday!

Eine häufig gestellte Frage lautet, wie man diese Ausgaben in Landessprache ausgeben kann. Zunächst ist es wichtig, das locale-Modul zu importieren:

from datetime import datetime, timedelta
import locale
# Umstellung auf Deutsch:
locale.setlocale(locale.LC_ALL, 'de_DE.utf8')
d1 = datetime(1993, 12, 14)
print(d1.strftime('%d. %B %Y'))
print("Nur in Zahlen:")
print(d1.strftime('%d.%m.%Y'))
print(f"Der {d1.strftime('%d.%m.%Y'):s} war ein {d1.strftime('%A'):s}!")
# und nun in Französisch:
      
locale.setlocale(locale.LC_ALL, 'fr_FR.utf8')
d1 = datetime(1993, 12, 14)
print(d1.strftime('%d %B %Y'))
print("Seulement en nombre:")
print(d1.strftime('%d.%m.%Y'))
print(f"Le {d1.strftime('%d %m %Y'):s} était un {d1.strftime('%A'):s}!")
14. Dezember 1993
Nur in Zahlen:
14.12.1993
Der 14.12.1993 war ein Dienstag!
14 décembre 1993
Seulement en nombre:
14.12.1993
Le 14 12 1993 était un mardi!

Anmerkung:

Die länderspezifischen Ausgaben der obigen Beispiele funktionieren nur, falls 'de_DE.utf8' und 'fr_FR.utf8' im Betriebssystem installiert sind. Unter Ubuntu kann man diese wie folgt installieren:

sudo locale-gen fr_FR.UTF-8

und

sudo locale-gen de_DE.UTF-8

datetime-Objekte aus Strings erstellen

Wir können strptime nutzen, um neue datetime-Objekte aus Strings zu erstellen, welche Datum und Zeit beinhalten. Die Argumente von strptime sind der String, welcher geparst werden soll, und die Format-Spezifikation.

from datetime import datetime
t = datetime.strptime("30 12 1999", "%d %m %Y")
print(t)
1999-12-30 00:00:00
dt = "2007-03-04T21:08:12"
datetime.strptime( dt, "%Y-%m-%dT%H:%M:%S" )
Ausgabe: :

datetime.datetime(2007, 3, 4, 21, 8, 12)
import locale
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
dt = '12/24/1957 4:03:29 AM'
dt = datetime.strptime(dt, '%m/%d/%Y %I:%M:%S %p')
dt
Ausgabe: :

datetime.datetime(1957, 12, 24, 4, 3, 29)

Auf einer Linux-Maschine können wir einen englischen Datumsstring generieren mit dem Kommando LC_ALL=en_EN.utf8 date.

dt = 'Wed Apr 12 20:29:53 CEST 2017'
dt = datetime.strptime(dt, '%a %b %d %H:%M:%S %Z %Y')
print(dt)
2017-04-12 20:29:53

Obwohl datetime.strptime() eine einfache Möglichkeit ist, ein Datum mit einem bekannten Format zu parsen, kann es doch kompliziert sein, für neue Datumsformate jedes Mal eine neue Spezifikation zu erstellen.

Für das Parsen ist die Nutzung der Methode dateutil.parser besser:

from dateutil.parser import parse
parse('2011-01-03')
Ausgabe: :

datetime.datetime(2011, 1, 3, 0, 0)
parse('Wed Apr 12 20:29:53 CEST 2017')
Ausgabe: :

datetime.datetime(2017, 4, 12, 20, 29, 53, tzinfo=tzlocal())