Python bietet unter anderem ein Modul namens datetime, um mit Datums- und Zeitangaben zu arbeiten. Lassen Sie uns ein paar einfache Programme erstellen, um in die Materie einzusteigen.
Nachfolgend greifen wir immer wieder auf das Modul datetime zu, sodass ich folgenden Import voraussetze:
>>> import datetime
Eine der wichtigsten im datetime-Modul definierten Klassen ist datetime.
Durch Aufruf der Funktion now() erhalten wir ein datetime-Objekt, das das aktuelle lokale Datum und die Uhrzeit (ohne Zeitzonenbezug) enthält. Zur einfacheren Handhabung erstellen wir eine gleichnamige Hilfsfunktion:
>>> def now():
... return datetime.datetime.now()
...
>>> now()
datetime.datetime(2023, 1, 8, 14, 31, 48, 945841)
>>> print(now())
2023-01-08 14:32:06.904744
Bitte beachten Sie die folgenden beiden Dinge:
Wir können ein datetime-Objekt basierend auf Angaben zu Jahr, Monat, Tag und optional auch Zeit erzeugen. Zunächst nutzen wir lediglich Jahr, Monat und Tag, wodurch die Zeitinformationen auf 0 gesetzt werden:
>>> date_no_time_set = datetime.datetime(2020, 11, 23)
>>> date_no_time_set
datetime.datetime(2020, 11, 23, 0, 0)
>>> print(date_no_time_set)
2020-11-23 00:00:00
Nun nutzen wir Datums- und Zeitinformationen:
>>> print(datetime.datetime(2017, 11, 28, 23, 55, 59, 342380))
2017-11-28 23:55:59.342380
Bekanntermaßen besteht ein Zeitpunkt aus diversen Bestandteilen. Auf diese können wir per .-Notation und deren Namen zugreifen, etwa auf year, hour und minute:
>>> sunday_afternoon = datetime.datetime(2021, 6, 13, 16, 52, 59)
>>>
>>> print("year =", sunday_afternoon.year)
year = 2021
>>> print("month =", sunday_afternoon.month)
month = 6
>>> print("day =", sunday_afternoon.day)
day = 13
>>> print("hour =", sunday_afternoon.hour)
hour = 16
>>> print("minute =", sunday_afternoon.minute)
minute = 52
>>> print("second =", sunday_afternoon.second)
second = 59
>>> print("timestamp =", sunday_afternoon.timestamp())
timestamp = 1623595979.0
Neben der Angabe der einzelnen Bestandteile können wir datetime-Objekte auch aus einem Unix-Zeitstempel erzeugen. Ein solcher Zeitstempel ist die Anzahl der Sekunden zwischen einem bestimmten Datum und dem 1. Januar 1970 in UTC. Die Methode fromtimestamp() wandelt einen Zeitstempel in ein Datum um – eine Rückwandlung geschieht mit timestamp():
>>> datetime_from_ts = datetime.datetime.fromtimestamp(43211234)
>>> print("Datetime =", datetime_from_ts)
Datetime = 1971-05-16 04:07:14
>>> datetime_from_ts.timestamp()
43211234.0
Jedes datetime-Objekt bietet Zugriff auf ein date-Objekt, das wiederum ein Datum repräsentiert.
Die Klasse date besitzt eine today()-Methode. Um den Zugriff innerhalb unserer Applikation noch etwas einfacher zu gestalten, erstellen wir eine gleichnamige Hilfsfunktion:
>>> def today():
... return datetime.date.today()
...
>>> today()
datetime.date(2023, 1, 8)
>>> print(today())
2023-01-08
Bitte beachten Sie nochmals die folgenden beiden Dinge:
Man kann date-Objekte durch drei Argumente erzeugen: Jahr, Monat und Tag, etwa wie nachfolgend für den Geburtstag von Sophie:
>>> sophies_birthday = datetime.date(2020, 11, 23)
>>> print(sophies_birthday)
2020-11-23
Das Ganze lässt sich alternativ auf Basis eines Strings gemäß dem korrespondierenden ISO-Format durch Aufruf von fromisoformat() lösen:
>>> datetime.date.fromisoformat('2020-11-23')
datetime.date(2020, 11, 23)
Basierend auf einem date-Objekt können wir Informationen zu Jahr, Monat und Tag durch Zugriff auf die korrespondierenden Attribute ermitteln:
>>> today_ = datetime.date.today()
>>>
>>> print("Current year: {} month: {} day: {}".format(today_.year, today_.month, today_.day))
Current year: 2023 month: 1 day: 8
Beachten Sie hier die Notwendigkeit, die Variable today_ mit _ endend anders als die Funktion zu benennen, damit man today() nicht verdeckt und erneut aufrufen kann.
Um den Wochentag zu einem Datum zu ermitteln, schreiben wir uns folgende Hilfsfunktion, die auf strftime() basiert (in Abschnitt 10.1.6 erfahren Sie mehr dazu):
>>> def get_week_day(date):
... return date.strftime('%A')
...
>>> get_week_day(datetime.date(1971, 2, 7))
'Sunday'
>>> get_week_day(datetime.date(1979, 7, 14))
'Saturday'
Für den Fall, dass wir Zugriff auf den numerischen Wert von 0 – Montag bis 6 – Sonntag benötigen, existiert die Methode weekday(). Ergänzend gibt es die Methode isoweekday() mit dem Wertebereich 1 bis 7 für Montag bis Sonntag.
>>> my_birthday = datetime.date(1971, 2, 7)
>>> my_birthday.weekday()
6
>>> my_birthday.isoweekday()
7
Für einige Anwendungsfälle müssen wir zu einem Datum wissen, der wievielte Tag im Monat dies ist. Dazu schreiben wir uns eine Hilfsfunktion day_of_month(), die das Attribut day zurückliefert:
>>> def day_of_month(date):
... return date.day
...
>>> day_of_month(datetime.date(1971, 2, 7))
7
Mitunter ist es von Bedeutung, zu einem Datum die Entsprechung als Tag im Jahr zu ermitteln. Dann implementieren wir wieder eine einfache Hilfsfunktion day_of_year(). Diese stützt sich diesmal auf timetuple() und das Attribut tm_yday ab:
>>> def day_of_year(date):
... return date.timetuple().tm_yday
...
>>> day_of_year(datetime.date(1971, 2, 7))
38
Alternative mit FormatierungMithilfe der später detaillierter behandelten Formatierung (vgl. Abschnitt 10.1.6) kann man dies alternativ zu der gerade erstellten Funktion wie folgt lösen:
>>> def day_of_year_2(date):
... return date.strftime('%j')
...
>>> day_of_year_2(datetime.date(1971, 2, 7))
'038'
Allerdings erhält man hier als Rückgabe einen String, den man zur weiteren Verarbeitung in der Regel noch geeignet in einen Zahlenwert konvertieren muss.
>>> int(day_of_year_2(datetime.date(1971, 2, 7)))
38
Aus einem Zeitstempel können wir nicht nur datetime-Objekte, sondern auch date-Objekte erzeugen. Die Methode fromtimestamp() wandelt einen Zeitstempel in ein Datum um:
>>> date_from_ts = datetime.date.fromtimestamp(43211234)
>>> print("Date =", date_from_ts)
Date = 1971-05-16
Für einige Anwendungsfälle ist es hilfreich, die Tage seit Christi Geburt fortlaufend als Zahlenwert zu zählen. Mit toordinal() wandelt man ein Datum in einen Zahlenwert, der der Anzahl an Tagen seit dem 1. Januar im Jahr 1 entspricht:
>>>>>> today = datetime.date.fromisoformat('2022-07-14')
>>> today.toordinal()
738350
>>> year_10 = datetime.date.fromisoformat('0010-07-14')
>>> year_10.toordinal()
3482
Mit fromordinal() erhält man ein date-Objekt zu dem korrespondierenden Zahlenwert:
>>> datetime.date.fromordinal(1)
datetime.date(1, 1, 1)
>>> datetime.date.fromordinal(700_000)
datetime.date(1917, 7, 15)
>>> datetime.date.fromordinal(750_000)
datetime.date(2054, 6, 6)
>>> datetime.date.fromordinal(720_000)
datetime.date(1972, 4, 17)
Jedes datetime-Objekt bietet Zugriff auf ein time-Objekt, was wiederum eine Zeitangabe repräsentiert.
Ein solches time-Objekt besitzt eine time()-Methode:
>>> now_ = datetime.datetime.now().time()
>>> print("now =", now_)
now = 15:14:26.100917
>>> print("type(now) =", type(now_))
type(now) = <class 'datetime.time'>
Beachten Sie hier die Notwendigkeit, die Variable now_ mit _ endend anders als die Funktion zu benennen, damit man now() nicht verdeckt und erneut aufrufen kann.
Ein time-Objekt modelliert eine Zeitangabe und bietet folgende Konstruktionsmöglichkeiten:
>>> datetime.time()
datetime.time(0, 0)
>>>
>>> datetime.time(11, 28, 45)
datetime.time(11, 28, 45)
>>>
>>> datetime.time(11, 28, 45, 6789)
datetime.time(11, 28, 45, 6789)
Schauen wir uns noch exemplarisch eine Ausgabe mit print() an:
>>> print(datetime.time(11, 28, 45))
11:28:45
Sobald Sie ein time-Objekt erstellt haben, können Sie dessen Attribute wie Stunde, Minute usw. einfach per Attribut auslesen und ausgeben:
>>> lunch_time = datetime.time(12, 34, 56)
>>>
>>> print("hour =", lunch_time.hour)
hour = 12
>>> print("minute =", lunch_time.minute)
minute = 34
>>> print("second =", lunch_time.second)
second = 56
>>> print("microsecond =", lunch_time.microsecond)
microsecond = 0
Mitunter muss man die Differenz zwischen zwei Zeitpunkten ermitteln. Dabei hilft die Klasse timedelta:
>>> start = datetime.datetime(2021, 2, 7, 10, 10, 10)
>>> end = datetime.datetime(2021, 2, 8, 11, 12, 13)
>>> end - start
datetime.timedelta(days=1, seconds=3723)
Interessanterweise lässt sich diese Berechnung der Differenz auch für Zeitspannen ausführen, selbst wenn diese eine unterschiedliche Granularität besitzen. Führen wir das Beispiel für zwei timedelta-Objekte fort. Das Ergebnis wird hier in den Einheiten von Tagen und Sekunden dargestellt:
>>> t1 = datetime.timedelta(weeks = 2, days = 3, hours = 5, seconds = 6)
>>> t2 = datetime.timedelta(days = 7, hours = 8, minutes = 9, seconds = 10)
>>> t3 = t1 - t2
>>> t3
datetime.timedelta(days=9, seconds=75056)
Neben Addition und Subtraktion kann man ein timedelta-Objekt auch mit Ganzzahlen und Fließkommazahlen multiplizieren und dividieren:
>>> t1 = datetime.timedelta(seconds = 33)
>>> t2 = datetime.timedelta(seconds = 54)
>>> diff1 = t1 - t2
>>> diff2 = t2 - t1
>>> sum = t1 + t2
>>> diff1
datetime.timedelta(days=-1, seconds=86379)
>>> diff2
datetime.timedelta(seconds=21)
>>> sum
datetime.timedelta(seconds=87)
>>> diff2 * 5
datetime.timedelta(seconds=105)
>>> diff2 / 2
datetime.timedelta(seconds=10, microseconds=500000)
Basierend auf einem Zeitpunkt als date und einer Zeitdifferenz als timedelta lassen sich »Zeitsprünge« vornehmen. Das ist nachfolgend für den Geburtstag des Autors sowie eine Zeitspanne von 8 Wochen und 31 Tagen verdeutlicht:
>>> michas_birthday = datetime.date(1971, 2, 7)
>>> print(michas_birthday)
1971-02-07
>>>
>>> print(michas_birthday + datetime.timedelta(weeks=8, days=31))
1971-05-05
Zum Einstieg in das Thema Berechnungen mit Datumswerten schauen wir uns ein Beispiel an, das noch einmal vergegenwärtigt, dass man zwei Datumswerte mit dem Operator - voneinander subtrahieren kann. Als Ergebnis erhält man ein timedelta-Objekt. Dieses wiederum beschreibt die zeitliche Differenz über die Attribute days und seconds, wobei wir hier nur Ersteres nutzen. Nachfolgend bestimmen wir die Länge des Februars für ein normales Jahr (2014) und ein Schaltjahr (2012):
>>> print((datetime.date(2014, 3, 1) - datetime.date(2014, 2, 1)).days)
28
>>> print((datetime.date(2012, 3, 1) - datetime.date(2012, 2, 1)).days)
29
Wie erwartet, ändert sich die Länge des Februars abhängig vom Schaltjahr von 28 auf 29 Tage. Die Länge der anderen Monate bleibt erwartungskonform gleich.
Ab und an muss man ermitteln, wie viele Tage ein Monat hat. Basierend auf dem initial gewonnenen Wissen lagert man die Berechnung der Monatslänge zweckmäßigerweise in folgende Funktion aus:
>>> def length_of_month(month, year):
... start = datetime.date(year, month, 1)
...
... month += 1
... if month > 12:
... year += 1
... month = 1
...
... end = datetime.date(year, month, 1)
...
... return (end - start).days
...
>>>
Berechnen wir erneut die Länge des Februars für die beiden Jahre 2012 und 2014:
>>> print(length_of_month(2, 2012))
29
>>> print(length_of_month(2, 2014))
28
Ebenso wie die Monatslänge kann die Länge eines Jahres von Interesse sein. Dabei hilft wieder die Abfrage mit days. Diesmal messen wir einfach die Differenz zwischen dem ersten Januar des gewünschten und des Folgejahres:
>>> print((datetime.date(2013, 1, 1) - datetime.date(2012, 1, 1)).days)
366
>>> print((datetime.date(2015, 1, 1) - datetime.date(2014, 1, 1)).days)
365
Das lagert man zweckmäßigerweise in folgende Funktion aus:
>>> def length_of_year(year):
... start = datetime.date(year, 1, 1)
... end = datetime.date(year + 1, 1, 1)
... return (end - start).days
...
Prüfen wir dies einmal für die Jahre 2012 und 2014 nach:
>>> print(length_of_year(2012))
366
>>> print(length_of_year(2014))
365
Es ist wünschenswert, programmatisch ermitteln zu können, ob ein Jahr ein Schaltjahr ist. Dabei hilft uns das Modul calendar und die Funktion isleap(). Das schauen wir uns für die zuvor genutzten Jahre an:
>>> import calendar
>>>
>>> print(calendar.isleap(2012))
True
>>> print(calendar.isleap(2014))
False
Die Art und Weise, wie Datum und Uhrzeit dargestellt werden, variiert von Land zu Land. Während man in Deutschland ein Datum im Format dd.mm.YYYY nutzt (mit den Platzhaltern d für Tage, m für Monate, Y für Jahre), so ist in den USA das Format mm/dd/YYYY üblich, während in Großbritannien eher dd/mm/YYYY gebräuchlich ist.
Python bietet die Methoden strftime() und strptime(), um die Formatierung und das Parsing zu handhaben.
Die Methode strftime() ist für die Klassen date, datetime und time definiert. Die Methode erzeugt basierend auf einem String mit Platzhaltern eine formatierte Repräsentation für ein gegebenes date-, datetime- oder time-Objekt.
Im folgenden Beispiel nutzen wir die eingangs vorgestellte und hier nochmals aufgeführte Funktion now() zum Ermitteln des aktuellen Zeitpunkts, um ein paar Formatierungsvarianten kennenzulernen:1
>>> def now():
... return datetime.datetime.now()
...
>>> now_ = now()
>>>
>>> print(now_.strftime("%H:%M:%S"))
13:21:06
>>> print(now_.strftime("%d.%m.%Y, %H:%M:%S"))
13.06.2021, 13:21:06
>>>
>>> print("US:", now_.strftime("%m/%d/%Y, %H:%M:%S"))
US: 06/13/2021, 13:21:06
>>>
>>> print("GB:", now_.strftime("%d/%m/%Y, %H:%M:%S"))
GB: 13/06/2021, 13:21:06
Gebräuchliche und wesentliche Platzhalter sind folgende:
Die Methode strptime() erzeugt aus einer gegebenen Zeichenkette (die Datum und gegebenenfalls Uhrzeit darstellt) ein datetime-Objekt. Damit die Informationen geeignet extrahiert werden können, wird über einen String mit Platzhaltern der erwartete Aufbau der Eingabedaten spezifiziert:
>>> date_string = "14 July, 1979"
>>> date_object = datetime.datetime.strptime(date_string, "%d %B, %Y")
>>> print(date_object)
1979-07-14 00:00:00