| Britta Kröhl

PHP: date() & die Kalenderwoche

Kalenderwochen in PHP: ein schönes Thema. Man wirft einen Blick in die date()-Dokumentation, sieht ein großes W für die Kalenderwoche nach ISO-8601 und freut sich. Das Jahr noch dazu per Y und alles ist gut. Bis zum Jahreswechsel, an dem es plötzlich unerwartete Ergebnisse gibt.

Nehmen wir als Beispiel das Datum „31.12.2018“ und geben dieses per date(„W / Y“, $date) – also: „Kalenderwoche / Jahr“ aus. Ergebnis: „01 / 2018“.
Anderes Beispiel: der „01.01.2021“, gleiches Ausgabeformat, Ergebnis: „53 / 2021“.

Die Definition der Kalenderwochen sagt klar: „Der 4. Januar ist immer in Kalenderwoche 1.“ – orientieren wir uns daran und nehmen die Beispiele mal auseinander.

Der 31.12.2018 ist ein Montag, er gehört also zu der Woche, in der der 4. Januar vorkommt (der folgende Freitag) und ist damit korrekt die 1. Kalenderwoche des Jahres. Nur eben: nicht des Jahres 2018.
Der 01.01.2021 hingegen ist ein Freitag, und der 4. Januar des Jahres ein Montag – damit gehört dieser Freitag noch zum vorhergehenden Jahr und ist damit korrekt die Kalenderwoche 53. Nur eben: nicht des Jahres 2021.

Das Problem dürfte damit klar sein: versucht man Kalenderwoche und Jahr per date() auszugeben kann das zum Jahreswechsel zu Problemen führen – tatsächlich ist das fast zu jedem Jahreswechsel der Fall.

Interessanter wird jetzt natürlich die Lösung: man könnte anfangen herumzurechnen, einzelne Tage zu prüfen oder ähnliches… oder man macht sich genau diesen „Fehler“ zu Nutze und stellt zwei grundlegende Dinge fest:

  1. Es kann niemals die 52 oder 53 KW des Jahres sein, wenn es Januar ist – dann gehört die KW zum letzten Jahr.
  2. Es kann niemals die 1 KW des Jahres sein, wenn es Dezember ist – dann gehört die KW zum nächsten Jahr.

Auf dieser Überlegung basierend eine kleine Funktion:

function getCwYear ($date = "1546210800")
{
	$year = date("Y", $date);
	if (date("m", $date) == "01" && (date("W", $date) == 52 || date("W", $date) == 53))
		$year--;
	else if (date("m", $date) == "12" && date("W", $date) == 01)
		$year++;
	return $year;
}

– diese Funktion erwartet einen Timestamp, im Beispiel vorausgefüllt den für den 31.12.2018.

Damit haben wir dieses kleine Problem elegant und sicher gelöst – diese Funktion liefert jetzt immer das korrekte Jahr zur Kalenderwoche zurück.

Um das zu überprüfen hier noch ein kleines Script zur Darstellung der Jahreswechsel zwischen 1980 und 2030:

for ($year = 1980; $year < 2030; $year++)
{
	$date = strtotime("28.12.".$year);
	for ($i = 0; $i < 7; $i++)
	{
		$date = strtotime("+1 day", $date);
		$new_year = getCwYear($date);
		
		if ($new_year != date("Y", $date))
			echo date("D, d.m.Y: W / Y", $date).' => '.$new_year;
		else 
			echo date("D, d.m.Y: W / Y", $date);
	}
}

…sowie einen beispielhaften Screenshot der Ausgabe, in diesem Fall eingefärbt.