
Bild vom Autor
# Einführung
Das Parsen von Datums- und Uhrzeitangaben ist eine dieser Aufgaben, die einfach erscheinen, bis Sie es tatsächlich versuchen. Pythons Datetime-Modul verarbeitet Standardformate intestine, aber reale Daten sind chaotisch. Benutzereingaben, gescrapte Webdaten und veraltete Systeme werfen oft Probleme auf.
Dieser Artikel führt Sie durch fünf praktische Funktionen zur Bewältigung gängiger Datums- und Uhrzeit-Parsing-Aufgaben. Am Ende werden Sie verstehen, wie Sie versatile Parser erstellen, die mit den chaotischen Datumsformaten umgehen, die Sie in Projekten sehen.
# 1. Analysieren relativer Zeitzeichenfolgen
Social-Media-Apps, Chat-Anwendungen und Aktivitäts-Feeds zeigen Zeitstempel wie „vor 5 Minuten“ oder „vor 2 Tagen“ an. Wenn Sie diese Daten extrahieren oder verarbeiten, müssen Sie diese relativen Zeichenfolgen wieder in tatsächliche Zeichenfolgen umwandeln datetime Objekte.
Hier ist eine Funktion, die gängige relative Zeitausdrücke verarbeitet:
from datetime import datetime, timedelta
import re
def parse_relative_time(time_string, reference_time=None):
"""
Convert relative time strings to datetime objects.
Examples: "2 hours in the past", "3 days in the past", "1 week in the past"
"""
if reference_time is None:
reference_time = datetime.now()
# Normalize the string
time_string = time_string.decrease().strip()
# Sample: quantity + time unit + "in the past"
sample = r'(d+)s*(second|minute|hour|day|week|month|yr)s?s*in the past'
match = re.match(sample, time_string)
if not match:
increase ValueError(f"Can't parse: {time_string}")
quantity = int(match.group(1))
unit = match.group(2)
# Map models to timedelta kwargs
unit_mapping = {
'second': 'seconds',
'minute': 'minutes',
'hour': 'hours',
'day': 'days',
'week': 'weeks',
}
if unit in unit_mapping:
delta_kwargs = {unit_mapping(unit): quantity}
return reference_time - timedelta(**delta_kwargs)
elif unit == 'month':
# Approximate: 30 days per 30 days
return reference_time - timedelta(days=quantity * 30)
elif unit == 'yr':
# Approximate: twelve months per yr
return reference_time - timedelta(days=quantity * 365)
Die Funktion verwendet a regulärer Ausdruck (regex) um die Zahl und die Zeiteinheit aus der Zeichenfolge zu extrahieren. Das Muster (d+) erfasst eine oder mehrere Ziffern und (second|minute|hour|day|week|month|yr) entspricht der Zeiteinheit. Der s? macht den Plural „s“ non-compulsory, sodass sowohl „hour“ als auch „hours“ funktionieren.
Für Einheiten, die Zeitdelta unterstützt direkt (Sekunden bis Wochen), wir erstellen eine timedelta und subtrahieren Sie es von der Referenzzeit. Für Monate und Jahre gehen wir von 30 bzw. 365 Tagen aus. Das ist nicht perfekt, aber für die meisten Anwendungsfälle intestine genug.
Der reference_time Mit dem Parameter können Sie ein anderes „Jetzt“ für Assessments oder die Verarbeitung historischer Daten angeben.
Lass es uns testen:
result1 = parse_relative_time("2 hours in the past")
result2 = parse_relative_time("3 days in the past")
result3 = parse_relative_time("1 week in the past")
print(f"2 hours in the past: {result1}")
print(f"3 days in the past: {result2}")
print(f"1 week in the past: {result3}")
Ausgabe:
2 hours in the past: 2026-01-06 12:09:34.584107
3 days in the past: 2026-01-03 14:09:34.584504
1 week in the past: 2025-12-30 14:09:34.584558
# 2. Extrahieren von Daten aus Textual content in natürlicher Sprache
Manchmal muss man im Textual content versteckte Termine finden: „Das Treffen ist für den 15. Januar 2026 geplant“ oder „Bitte antworten Sie bis zum 3. März“. Anstatt den gesamten Satz manuell zu analysieren, möchten Sie nur das Datum extrahieren.
Hier ist eine Funktion, die Daten aus natürlicher Sprache findet und extrahiert:
import re
from datetime import datetime
def extract_date_from_text(textual content, current_year=None):
"""
Extract dates from pure language textual content.
Handles codecs like:
- "January fifteenth, 2024"
- "March third"
- "Dec twenty fifth, 2023"
"""
if current_year is None:
current_year = datetime.now().yr
# Month names (full and abbreviated)
months = {
'january': 1, 'jan': 1,
'february': 2, 'feb': 2,
'march': 3, 'mar': 3,
'april': 4, 'apr': 4,
'could': 5,
'june': 6, 'jun': 6,
'july': 7, 'jul': 7,
'august': 8, 'aug': 8,
'september': 9, 'sep': 9, 'sept': 9,
'october': 10, 'oct': 10,
'november': 11, 'nov': 11,
'december': 12, 'dec': 12
}
# Sample: Month Day(st/nd/rd/th), 12 months (yr non-compulsory)
sample = r'(january|jan|february|feb|march|mar|april|apr|could|june|jun|july|jul|august|aug|september|sep|sept|october|oct|november|nov|december|dec)s+(d{1,2})(?:st|nd|rd|th)?(?:,?s+(d{4}))?'
matches = re.findall(sample, textual content.decrease())
if not matches:
return None
# Take the primary match
month_str, day_str, year_str = matches(0)
month = months(month_str)
day = int(day_str)
yr = int(year_str) if year_str else current_year
return datetime(yr, month, day)
Die Funktion erstellt ein Wörterbuch, das Monatsnamen (sowohl vollständige als auch abgekürzte) ihren numerischen Werten zuordnet. Das Regex-Muster gleicht Monatsnamen gefolgt von Tageszahlen mit optionalen Ordnungssuffixen (st, nd, rd, th) und einem optionalen Jahr ab.
Der (?:...) Die Syntax erstellt eine nicht erfassende Gruppe. Das bedeutet, dass wir das Muster abgleichen, es aber nicht separat speichern. Dies ist nützlich für optionale Teile wie die Ordinalsuffixe und das Jahr.
Wenn kein Jahr angegeben wird, verwendet die Funktion standardmäßig das aktuelle Jahr. Das ist logisch, denn wenn jemand im Januar den „3. März“ erwähnt, bezieht er sich normalerweise auf den kommenden März und nicht auf den des Vorjahres.
Testen wir es mit verschiedenen Textformaten:
text1 = "The assembly is scheduled for January fifteenth, 2026 at 3pm"
text2 = "Please reply by March third"
text3 = "Deadline: Dec twenty fifth, 2026"
date1 = extract_date_from_text(text1)
date2 = extract_date_from_text(text2)
date3 = extract_date_from_text(text3)
print(f"From '{text1}': {date1}")
print(f"From '{text2}': {date2}")
print(f"From '{text3}': {date3}")
Ausgabe:
From 'The assembly is scheduled for January fifteenth, 2026 at 3pm': 2026-01-15 00:00:00
From 'Please reply by March third': 2026-03-03 00:00:00
From 'Deadline: Dec twenty fifth, 2026': 2026-12-25 00:00:00
# 3. Parsen flexibler Datumsformate mit intelligenter Erkennung
Reale Daten gibt es in vielen Formaten. Das Schreiben separater Parser für jedes Format ist mühsam. Stattdessen erstellen wir eine Funktion, die mehrere Formate automatisch ausprobiert.
Hier ist ein intelligenter Datumsparser, der gängige Formate verarbeitet:
from datetime import datetime
def parse_flexible_date(date_string):
"""
Parse dates in a number of widespread codecs.
Tries varied codecs and returns the primary match.
"""
date_string = date_string.strip()
# Record of widespread date codecs
codecs = (
'%Y-%m-%d',
'%Y/%m/%d',
'%d-%m-%Y',
'%d/%m/%Y',
'%m/%d/%Y',
'%d.%m.%Y',
'%Ypercentmpercentd',
'%B %d, %Y',
'%b %d, %Y',
'%d %B %Y',
'%d %b %Y',
)
# Strive every format
for fmt in codecs:
strive:
return datetime.strptime(date_string, fmt)
besides ValueError:
proceed
# If nothing labored, increase an error
increase ValueError(f"Unable to parse date: {date_string}")
Diese Funktion verwendet einen Brute-Power-Ansatz. Es probiert jedes Format aus, bis eines funktioniert. Der strptime Funktion löst a aus ValueError Wenn die Datumszeichenfolge nicht mit dem Format übereinstimmt, fangen wir diese Ausnahme ab und gehen zum nächsten Format über.
Die Reihenfolge der Formate ist wichtig. Wir haben das Format der Worldwide Group for Standardization (ISO) verwendet (%Y-%m-%d) Erstens, weil es in technischen Zusammenhängen am häufigsten vorkommt. Mehrdeutige Formate wie %d/%m/%Y Und %m/%d/%Y später erscheinen. Wenn Sie wissen, dass Ihre Daten einen konsistent verwenden, ordnen Sie die Liste neu an, um sie zu priorisieren.
Testen wir es mit verschiedenen Datumsformaten:
# Take a look at completely different codecs
dates = (
"2026-01-15",
"15/01/2026",
"01/15/2026",
"15.01.2026",
"20260115",
"January 15, 2026",
"15 Jan 2026"
)
for date_str in dates:
parsed = parse_flexible_date(date_str)
print(f"{date_str:20} -> {parsed}")
Ausgabe:
2026-01-15 -> 2026-01-15 00:00:00
15/01/2026 -> 2026-01-15 00:00:00
01/15/2026 -> 2026-01-15 00:00:00
15.01.2026 -> 2026-01-15 00:00:00
20260115 -> 2026-01-15 00:00:00
January 15, 2026 -> 2026-01-15 00:00:00
15 Jan 2026 -> 2026-01-15 00:00:00
Dieser Ansatz ist zwar nicht der effizienteste, aber er ist einfach und kommt mit den meisten Datumsformaten zurecht, denen Sie begegnen werden.
# 4. Parsing-Zeitdauern
Videoplayer, Trainings-Tracker und Zeiterfassungs-Apps zeigen Dauern wie „1h 30m“ oder „2:45:30“ an. Wenn Sie Benutzereingaben oder Scraping-Daten analysieren, müssen Sie diese konvertieren timedelta Objekte für Berechnungen.
Hier ist eine Funktion, die gängige Dauerformate analysiert:
from datetime import timedelta
import re
def parse_duration(duration_string):
"""
Parse period strings into timedelta objects.
Handles codecs like:
- "1h 30m 45s"
- "2:45:30" (H:M:S)
- "90 minutes"
- "1.5 hours"
"""
duration_string = duration_string.strip().decrease()
# Strive colon format first (H:M:S or M:S)
if ':' in duration_string:
elements = duration_string.break up(':')
if len(elements) == 2:
# M:S format
minutes, seconds = map(int, elements)
return timedelta(minutes=minutes, seconds=seconds)
elif len(elements) == 3:
# H:M:S format
hours, minutes, seconds = map(int, elements)
return timedelta(hours=hours, minutes=minutes, seconds=seconds)
# Strive unit-based format (1h 30m 45s)
total_seconds = 0
# Discover hours
hours_match = re.search(r'(d+(?:.d+)?)s*h(?:ours?)?', duration_string)
if hours_match:
total_seconds += float(hours_match.group(1)) * 3600
# Discover minutes
minutes_match = re.search(r'(d+(?:.d+)?)s*m(?:in(?:ute)?s?)?', duration_string)
if minutes_match:
total_seconds += float(minutes_match.group(1)) * 60
# Discover seconds
seconds_match = re.search(r'(d+(?:.d+)?)s*s(?:ec(?:ond)?s?)?', duration_string)
if seconds_match:
total_seconds += float(seconds_match.group(1))
if total_seconds > 0:
return timedelta(seconds=total_seconds)
increase ValueError(f"Unable to parse period: {duration_string}")
Die Funktion verarbeitet zwei Hauptformate: durch Doppelpunkte getrennte Zeitangaben und einheitenbasierte Zeichenfolgen. Beim Doppelpunktformat teilen wir den Doppelpunkt auf und interpretieren die Teile als Stunden, Minuten und Sekunden (oder nur Minuten und Sekunden für zweiteilige Dauern).
Für das einheitenbasierte Format verwenden wir drei separate Regex-Muster, um Stunden, Minuten und Sekunden zu finden. Das Muster (d+(?:.d+)?) Entspricht ganzen Zahlen oder Dezimalzahlen wie „1,5“. Das Muster s*h(?:ours?)? Entspricht „h“, „hour“ oder „hours“ mit optionalem Leerzeichen.
Jeder übereinstimmende Wert wird in Sekunden umgerechnet und zur Gesamtsumme addiert. Mit diesem Ansatz kann die Funktion Teildauern wie „45 Sekunden“ oder „2 Stunden 15 Minuten“ verarbeiten, ohne dass alle Einheiten vorhanden sein müssen.
Testen wir die Funktion nun mit verschiedenen Dauerformaten:
durations = (
"1h 30m 45s",
"2:45:30",
"90 minutes",
"1.5 hours",
"45s",
"2h 15m"
)
for period in durations:
parsed = parse_duration(period)
print(f"{period:15} -> {parsed}")
Ausgabe:
1h 30m 45s -> 1:30:45
2:45:30 -> 2:45:30
90 minutes -> 1:30:00
1.5 hours -> 1:30:00
45s -> 0:00:45
2h 15m -> 2:15:00
# 5. Analysieren von ISO-Wochendaten
Einige Systeme verwenden ISO-Wochendaten anstelle regulärer Kalenderdaten. Ein ISO-Wochendatum wie „2026-W03-2“ bedeutet „Woche 3 von 2026, Tag 2 (Dienstag)“. Dieses Format ist in Geschäftskontexten üblich, in denen die Planung wöchentlich erfolgt.
Hier ist eine Funktion zum Analysieren von ISO-Wochendaten:
from datetime import datetime, timedelta
def parse_iso_week_date(iso_week_string):
"""
Parse ISO week date format: YYYY-Www-D
Instance: "2024-W03-2" = Week 3 of 2024, Tuesday
ISO week numbering:
- Week 1 is the week with the primary Thursday of the yr
- Days are numbered 1 (Monday) by way of 7 (Sunday)
"""
# Parse the format: YYYY-Www-D
elements = iso_week_string.break up('-')
if len(elements) != 3 or not elements(1).startswith('W'):
increase ValueError(f"Invalid ISO week format: {iso_week_string}")
yr = int(elements(0))
week = int(elements(1)(1:)) # Take away 'W' prefix
day = int(elements(2))
if not (1 <= week <= 53):
increase ValueError(f"Week have to be between 1 and 53: {week}")
if not (1 <= day <= 7):
increase ValueError(f"Day have to be between 1 and seven: {day}")
# Discover January 4th (all the time in week 1)
jan_4 = datetime(yr, 1, 4)
# Discover Monday of week 1
week_1_monday = jan_4 - timedelta(days=jan_4.weekday())
# Calculate the goal date
target_date = week_1_monday + timedelta(weeks=week - 1, days=day - 1)
return target_date
ISO-Wochentermine unterliegen bestimmten Regeln. Woche 1 ist die Woche, die den ersten Donnerstag des Jahres enthält. Das bedeutet, dass Woche 1 möglicherweise im Dezember des Vorjahres beginnt.
Die Funktion verwendet einen zuverlässigen Ansatz: Finden Sie den 4. Januar (der immer in Woche 1 liegt) und dann den Montag dieser Woche. Von dort aus addieren wir die entsprechende Anzahl an Wochen und Tagen, um das Zieldatum zu erreichen.
Die Berechnung jan_4.weekday() gibt 0 für Montag bis 6 für Sonntag zurück. Wenn wir dies vom 4. Januar subtrahieren, erhalten wir den Montag der ersten Woche. Dann addieren wir (week - 1) Wochen und (day - 1) Tage, um den endgültigen Termin zu erfahren.
Lass es uns testen:
# Take a look at ISO week dates
iso_dates = (
"2024-W01-1", # Week 1, Monday
"2024-W03-2", # Week 3, Tuesday
"2024-W10-5", # Week 10, Friday
)
for iso_date in iso_dates:
parsed = parse_iso_week_date(iso_date)
print(f"{iso_date} -> {parsed.strftime('%Y-%m-%d (%A)')}")
Ausgabe:
2024-W01-1 -> 2024-01-01 (Monday)
2024-W03-2 -> 2024-01-16 (Tuesday)
2024-W10-5 -> 2024-03-08 (Friday)
Dieses Format ist weniger verbreitet als normale Datumsangaben, aber wenn es auftritt, spart die Verfügbarkeit eines Parsers erhebliche Zeit.
# Zusammenfassung
Jede Funktion in diesem Artikel verwendet Regex-Muster und Datum/Uhrzeit-Arithmetik, um Variationen in der Formatierung zu verarbeiten. Diese Techniken lassen sich auf andere Parsing-Herausforderungen übertragen, da Sie diese Muster für benutzerdefinierte Datumsformate in Ihren Projekten anpassen können.
Durch die Erstellung eigener Parser können Sie besser verstehen, wie das Datums-Parsing funktioniert. Wenn Sie auf ein nicht standardmäßiges Datumsformat stoßen, das Standardbibliotheken nicht verarbeiten können, sind Sie bereit, eine benutzerdefinierte Lösung zu schreiben.
Diese Funktionen sind besonders nützlich für kleine Skripte, Prototypen und Lernprojekte, bei denen das Hinzufügen starker externer Abhängigkeiten möglicherweise übertrieben ist. Viel Spaß beim Codieren!
Bala Priya C ist ein Entwickler und technischer Redakteur aus Indien. Sie arbeitet gerne an der Schnittstelle von Mathematik, Programmierung, Datenwissenschaft und Inhaltserstellung. Zu ihren Interessen- und Fachgebieten gehören DevOps, Datenwissenschaft und Verarbeitung natürlicher Sprache. Sie liebt es zu lesen, zu schreiben, zu programmieren und Kaffee zu trinken! Derzeit arbeitet sie daran, zu lernen und ihr Wissen mit der Entwickler-Group zu teilen, indem sie Tutorials, Anleitungen, Meinungsbeiträge und mehr verfasst. Bala erstellt außerdem ansprechende Ressourcenübersichten und Programmier-Tutorials.
