date_sub()
PHP erleichtert Datumsberechnungen mit DateTime::sub() und DateInterval – so ziehen Sie Zeitspannen zuverlässig von einem Datum ab.
Zeit von einem Datum in PHP subtrahieren
Eine Zeitspanne von einem Datum abzuziehen — „vor 30 Tagen", „Anfang letzten Monats", „zwei Stunden vor jetzt" — gehört zu den häufigsten Datumsaufgaben in PHP. Die alte prozedurale Funktion date_sub() (ein Alias von DateTime::sub()) wurde in PHP 8.0 entfernt, sodass moderner Code die objektorientierte Methode DateTime::sub() (oder DateTimeImmutable::sub()) zusammen mit einem DateInterval verwenden sollte.
Diese Seite erklärt, wie DateTime::sub() funktioniert, wie man den Intervall-String aufbaut, den Unterschied zwischen veränderbaren und unveränderbaren Objekten und die Kalender-Fallstricke (Monatsüberlauf, invertierte Intervalle, Sommerzeit), über die viele stolpern.
Wie DateTime::sub() funktioniert
sub() erwartet ein einzelnes DateInterval-Argument, das beschreibt, wie viel Zeit abgezogen werden soll. Die Signatur lautet:
public DateTime::sub(DateInterval $interval): DateTimeZwei wichtige Punkte:
- Es verändert das Objekt direkt — das ursprüngliche
DateTime-Objekt wird geändert. - Es gibt dasselbe Objekt zurück, sodass Aufrufe verkettet werden können.
$date = new DateTime('2023-10-15', new DateTimeZone('UTC'));
$interval = new DateInterval('P5D'); // 5 days
$date->sub($interval);
echo $date->format('Y-m-d'); // Outputs: 2023-10-10Die Angabe einer expliziten DateTimeZone macht das Ergebnis vorhersehbar; ohne sie verwendet PHP die Zeitzone aus date_default_timezone_set() oder php.ini.
Den DateInterval-String aufbauen
DateInterval verwendet das ISO-8601-Dauerformat: P[n]Y[n]M[n]DT[n]H[n]M[n]S. Das Präfix P (Period) ist obligatorisch, und ein T trennt den Datumsteil vom Zeitteil.
| Token | Bedeutung | Beispiel | Dauer |
|---|---|---|---|
Y | Jahre | P3Y | 3 Jahre |
M | Monate | P6M | 6 Monate |
D | Tage | P10D | 10 Tage |
H | Stunden | PT4H | 4 Stunden |
M | Minuten | PT30M | 30 Minuten |
S | Sekunden | PT45S | 45 Sekunden |
Beachten Sie, dass M vor dem T Monate bedeutet und nach dem T Minuten — eine häufige Fehlerquelle. Kombinieren Sie Token, um mehrere Einheiten gleichzeitig abzuziehen:
$date = new DateTime('2023-10-15 12:00:00');
$interval = new DateInterval('P2M1DT3H'); // 2 months, 1 day, 3 hours
$date->sub($interval);
echo $date->format('Y-m-d H:i:s'); // Outputs: 2023-08-14 09:00:00Rein zeitbasierte Intervalle funktionieren genauso — hier 90 Minuten:
$date = new DateTime('2023-10-15 08:30:00');
$date->sub(new DateInterval('PT90M')); // 90 minutes
echo $date->format('Y-m-d H:i:s'); // Outputs: 2023-10-15 07:00:00Hinweis:
DateIntervalprüft das Format streng. Ein ungültiger string (zum Beispiel ein fehlendesPoderPTohne Zeittoken) wirft eineException. Wenn die Dauer aus Benutzereingaben stammt, schließen Sie den Konstruktor in einentry...catch-Block ein.
Veränderbar vs. unveränderbar: DateTimeImmutable bevorzugen
Da DateTime::sub() das Objekt direkt verändert, kann ein DateTime, das Sie weitergeben, unerwartet von Code geändert werden, der sub() darauf aufruft. DateTimeImmutable löst dieses Problem: Seine sub()-Methode lässt das Original unverändert und gibt eine neue Instanz zurück.
$date = new DateTimeImmutable('2023-10-15');
$earlier = $date->sub(new DateInterval('P10D'));
echo $date->format('Y-m-d'); // Outputs: 2023-10-15 (unchanged)
echo "\n";
echo $earlier->format('Y-m-d'); // Outputs: 2023-10-05Für den größten Teil des Anwendungscodes sollten Sie standardmäßig DateTimeImmutable verwenden und das veränderbare DateTime nur dann einsetzen, wenn Sie gezielte direkte Änderungen benötigen.
Fallstricke, die Sie kennen sollten
Monatsüberlauf
Das Subtrahieren ganzer Monate begrenzt sich nicht auf den letzten Tag des Zielmonats — PHP normalisiert den Überlauf in den nächsten Monat. Das Subtrahieren eines Monats vom 31. März landet im März, nicht im Februar:
$date = new DateTime('2023-03-31');
$date->sub(new DateInterval('P1M'));
echo $date->format('Y-m-d'); // Outputs: 2023-03-03Hier geht P1M zunächst zum „31. Februar", den PHP auf den 3. März rollt (Februar hat 2023 nur 28 Tage). Wenn Sie den letzten Tag des Vormonats benötigen, verwenden Sie stattdessen einen relativen string: new DateTime('last day of previous month').
Invertierte Intervalle addieren statt subtrahieren
Ein DateInterval hat eine invert-Eigenschaft. Ist diese 1, ist das Intervall negativ, sodass sub() die Dauer effektiv addiert:
$interval = new DateInterval('P1M');
$interval->invert = 1; // negative interval
$date = new DateTime('2023-10-15');
$date->sub($interval);
echo $date->format('Y-m-d'); // Outputs: 2023-11-15Dies ist relevant, wenn Sie das von DateTime::diff() zurückgegebene Intervall wiederverwenden, da dieses invert automatisch je nach Richtung der Differenz setzt.
Sommerzeit
Wenn Sie Tage über eine Sommerzeitgrenze hinweg subtrahieren, passt PHP die Uhrzeit so an, dass das Kalender-Ergebnis korrekt bleibt. Wenn Sie stattdessen Stunden subtrahieren (PT24H), erhalten Sie genau 24 Stunden vergangene Zeit, was möglicherweise auf einer anderen Uhrzeit endet. Wählen Sie tages- oder stundenbasierte Intervalle bewusst danach, ob Sie „gleiche Uhrzeit, vorheriger Tag" oder „genau 24 Stunden früher" meinen.
Carbon für eine lesbare Syntax verwenden
Für große Codebasen kapselt die Carbon-Bibliothek DateTimeImmutable mit lesbaren, verkettbaren Hilfsmethoden. Sie ist optional — die nativen Klassen decken die Standardfälle ab — kann aber komplexe Logik klarer machen:
use Carbon\Carbon;
$date = Carbon::parse('2023-10-15');
$newDate = $date->subMonths(2)->subDays(5);
echo $newDate->toDateString(); // Outputs: 2023-08-10Fazit
Verwenden Sie DateTime::sub() (oder vorzugsweise DateTimeImmutable::sub()) mit einem DateInterval, um Zeitspannen von einem Datum abzuziehen. Erstellen Sie das Intervall im ISO-8601-Format P…T…, behalten Sie den Unterschied zwischen Monaten und Minuten bei M im Hinterkopf und achten Sie auf Monatsüberlauf und invertierte Intervalle.
Weitere Informationen finden Sie unter date_add() für die umgekehrte Operation, date_diff() zum Messen der Zeitspanne zwischen zwei Daten und date_format() zum Formatieren des Ergebnisses.