Das JavaScript Date-Objekt ist deine Standardbibliothek für Datum und Zeit in Browsern und Node.js. Es kann Millisekunden genau arbeiten, Datumswerte addieren und subtrahieren, zwischen lokaler Zeit und UTC unterscheiden und Strings in prüfbare Zeitwerte wandeln. Gleichzeitig ist es berüchtigt für Zero-based Monate, ISO-Parsing-Fallen und Zeitzonen-Tücken. Hier bekommst du einen umfassenden, praxisnahen Überblick mit Best Practices, Codebeispielen und sauberer Struktur, damit deine Arbeit mit Datum und Zeit stabil bleibt. Hinweis: In modernen asynchronen Szenarien lässt sich der Umgang mit Datum und Zeit auch hervorragend mit Async/Await integrieren.
Was Date intern repräsentiert
Ein Date speichert intern eine einzige Zahl: die Anzahl der Millisekunden seit dem 1. Januar 1970, 00:00:00 UTC (die Unix-Epoche). Dieser Wertebereich reicht gemäß ECMAScript-Spezifikation etwa ±100 Millionen Tage um diese Epoche herum, also grob ±273.785 Jahre. Alle Getter/Setter und Formatierungen leiten sich aus dieser Millisekunden-Zahl ab. Ein Date-Objekt läuft nicht “live” mit – es repräsentiert einen festen Zeitpunkt. Wenn du die “aktuelle Zeit” brauchst, erstelle ein neues Date oder nutze Date.now().
Merke: Date ist mutierbar. Methoden wie
setDate()odersetHours()ändern das bestehende Objekt in-place.
So erzeugst du ein Date-Objekt
Du hast mehrere Wege, ein JavaScript Date zu bauen. Die folgenden Varianten sind die wichtigsten:
| Signatur | Beschreibung | Beispiel | Hinweis |
|---|---|---|---|
new Date() |
Aktuelles Datum/Zeit (lokale Zeit) | const d = new Date(); |
Schnell für “jetzt” |
new Date(ms) |
Aus Millisekunden seit Epoche | new Date(0) |
UTC-basierte Epoche |
new Date(year, monthIndex, day?, h?, m?, s?, ms?) |
Numerische Komponenten (lokal) | new Date(2024, 0, 31, 23, 59) |
Monat ist 0–11 |
new Date(isoString) |
ISO 8601-String | new Date('2024-01-31T23:59:00Z') |
“Z” = UTC; sicherste String-Variante |
Zu den String-Varianten ist wichtig:
- ISO 8601, z. B.
2024-01-31T23:59:00Z, ist standardisiert und zuverlässig. Das “Z” kennzeichnet UTC. - Nur Datum im ISO-Format, z. B.
'2024-01-31', wird in modernen Engines als UTC interpretiert. Das kann zu “Überraschungen” führen, wenn du lokale Mitternacht erwartest. - Nicht standardisierte Strings wie
'12/31/2024'sind je nach Umgebung uneinheitlich – vermeide sie. - Arrays werden zu String konvertiert und führen meist zu
Invalid Date– nicht verwenden.
// korrektes ISO: UTC
const a = new Date('2024-01-31T23:59:00Z'); // UTC Zeitpunkt
// ISO ohne Zeit -> interpretiert als UTC
const b = new Date('2024-01-31'); // Achtung: Mitternacht UTC, nicht lokal
// numerisch (lokal):
const c = new Date(2024, 0, 31, 23, 59); // Jan (0), lokale Zeitzone
// aus Millisekunden:
const d = new Date(Date.now()); // äquivalent zu new Date()

Getter und Setter richtig nutzen
Die API unterscheidet zwischen lokalen und UTC-Methoden. Lokale Methoden beginnen mit get.../set...; UTC-Varianten mit getUTC.../setUTC....
| Methode | Lokale Variante | UTC-Variante | Rückgabe/Bereich | Hinweise |
|---|---|---|---|---|
| Jahr | getFullYear() |
getUTCFullYear() |
vierstellig | getYear() ist veraltet |
| Monat | getMonth() |
getUTCMonth() |
0–11 | Zero-based! Jan=0, Dez=11 |
| Tag des Monats | getDate() |
getUTCDate() |
1–31 | Nicht mit getDay() verwechseln |
| Wochentag | getDay() |
getUTCDay() |
0–6 (So=0) | So=0, Mo=1, … |
| Stunde/Minute/Sekunde | getHours() etc. |
getUTCHours() etc. |
0–23 / 0–59 | Standardwerte |
| Millisekunden | getMilliseconds() |
getUTCMilliseconds() |
0–999 | — |
| Time Value | getTime() |
— | Millisekunden seit Epoche | ≈ valueOf() |
Beim Setzen zeigt sich eine der größten Stärken von Date: automatisches “Rollover”. Übergibst du Werte außerhalb des Bereichs, korrigiert das Objekt Monat/Jahr entsprechend.
const d = new Date(2024, 0, 31); // 31. Jan 2024 (lokal)
d.setDate(d.getDate() + 1); // 1. Feb 2024
// Negativer oder "zu großer" Tag funktioniert ebenfalls:
const e = new Date(2024, 1, 1); // 1. Feb 2024
e.setDate(0); // 31. Jan 2024
Pro-Tipp: Nutze das Rollover-Verhalten für Monats-/Jahreswechsel. Du musst keine Monatslängen selbst berechnen.
Strings ausgeben: toLocale…, toISOString, Intl.DateTimeFormat
Für UI-Ausgaben brauchst du formatierte Strings. Zudem spielt das DOM eine zentrale Rolle bei der Einbindung von Datumswerten in Webanwendungen. Du hast drei Hauptwege:
- Lokalisierte Ausgabe mit
toLocaleDateString(),toLocaleTimeString(),toLocaleString(). - ISO 8601 mit
toISOString()für stabile, transportfähige Repräsentation. - Hohe Kontrolle und Performance mit
Intl.DateTimeFormat.
const d = new Date('2024-01-31T23:59:00Z');
// Lokalisierte Beispiele
d.toLocaleDateString('de-DE'); // "1.2.2024" (je nach TZ, da UTC->lokal)
d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }); // "00:59"
// ISO immer UTC mit 'Z':
d.toISOString(); // "2024-01-31T23:59:00.000Z"
// Wiederverwendbares Formatter-Objekt:
const fmt = new Intl.DateTimeFormat('de-DE', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
timeZone: 'Europe/Berlin'
});
fmt.format(d); // "Donnerstag, 1. Februar 2024 um 00:59"
Regel: Für Speicherung und API-Transport nutze ISO 8601 (
toISOString()) oder Zahlenzeitstempel. Für UI nutzeIntl.DateTimeFormatmit explizitertimeZone.
Zeitzonen, UTC und Sommerzeit
Standard-Date kennt genau zwei “Sichten”: lokale Zeit und UTC. Die lokale Zeitzone kommt vom System. Der Versatz zur UTC ist über getTimezoneOffset() zugänglich – in Minuten, mit Vorzeichen aus Sicht der lokalen Zeit.
const d = new Date();
const offsetMinutes = d.getTimezoneOffset(); // z. B. -60 für UTC+1
Sommerzeit (DST) macht Offsets dynamisch. Daher ist dieselbe Uhrzeit im Jahr nicht immer gleich versetzt. Für Berechnungen, die unabhängig von DST sein sollen, arbeite in UTC:
// Start des Tages in UTC:
function startOfDayUTC(d) {
const copy = new Date(d.getTime());
copy.setUTCHours(0, 0, 0, 0);
return copy;
}
Wenn du gezielt in einer bestimmten IANA-Zeitzone (z. B. Europe/Berlin, America/New_York) darstellen willst, nutze Intl.DateTimeFormat mit timeZone:
const d = new Date('2024-03-31T00:30:00Z');
new Intl.DateTimeFormat('de-DE', { timeZone: 'Europe/Berlin', timeStyle: 'short', dateStyle: 'medium' })
.format(d); // Beachtet die Zeitumstellung Ende März
Für UTC-Werte aus Komponenten existiert Date.UTC(). Es gibt die Millisekunden seit Epoche zurück, nicht ein Date-Objekt.
const utcMs = Date.UTC(2024, 0, 31, 23, 59); // Jan ist 0
const d = new Date(utcMs); // identischer Zeitpunkt in UTC

Zeitstempel, Unix-Zeit und Messungen
JavaScript nutzt Millisekunden seit Epoche. Unix-Timestamps sind meist in Sekunden. Die Konvertierung ist trivial:
// Date -> Unix Sekunden
const unix = Math.floor(Date.now() / 1000);
// Unix Sekunden -> Date
const fromUnix = (s) => new Date(s * 1000);
Für Zeitmessungen im Code gilt:
Date.now()ist gut für “wall-clock time”, kann sich aber ändern (Systemuhr, NTP).performance.now()ist monoton und hochauflösend – ideal für Benchmarks und Dauerberechnungen.
const t0 = performance.now();
// ... zu messender Code ...
const dt = performance.now() - t0; // Millisekunden, monotone Uhr
Datumsarithmetik: addieren, subtrahieren, differenzieren
Dank Rollover sind viele Aufgaben einfach:
// 30 Tage addieren (lokal)
function addDays(date, days) {
const d = new Date(date.getTime());
d.setDate(d.getDate() + days);
return d;
}
// N Stunden abziehen (UTC-sicher)
function addHoursUTC(date, hours) {
const d = new Date(date.getTime());
d.setUTCHours(d.getUTCHours() + hours);
return d;
}
Differenzen berechnest du über die Millisekunden:
// Differenz in Tagen (metrisch), Achtung: DST kann "Kalendertage" verzerren
function diffDays(a, b) {
const ms = a.getTime() - b.getTime();
return ms / 86400000; // 24*60*60*1000
}
// Kalender-tagesgenau (UTC-Normalisierung beugt DST-Fallen vor)
function diffCalendarDays(a, b) {
const uA = Date.UTC(a.getUTCFullYear(), a.getUTCMonth(), a.getUTCDate());
const uB = Date.UTC(b.getUTCFullYear(), b.getUTCMonth(), b.getUTCDate());
return Math.round((uA - uB) / 86400000);
}
Wichtig: Willst du “Anzahl Kalendertage” zwischen zwei Daten, normalisiere auf UTC-Mitternacht. Sonst verschieben Sommerzeitübergänge dein Ergebnis.
Spezialfälle: Schaltjahre, Invalid Date, Grenzen
Schaltjahrregeln: Teilbar durch 4 ist Schaltjahr, außer zugleich durch 100; wiederum doch Schaltjahr, wenn durch 400 teilbar. Javascript macht’s dir leicht:
function isLeapYear(year) {
const d = new Date(Date.UTC(year, 1, 29)); // 29. Februar in UTC
return d.getUTCMonth() === 1; // bleibt Februar?
}
Ungültige Eingaben führen zu Invalid Date. Prüfen kannst du über isNaN(d.getTime()):
const d = new Date('not-a-date');
if (isNaN(d.getTime())) {
console.error('Ungültiges Datum');
}
Zum Bereich: Die Spezifikation verlangt etwa ±8.64e15 ms um die Epoche, was sehr weite Bereiche abdeckt. In der Praxis sind extrem große Werte selten sinnvoll.
Best Practices kompakt
- Speichern/Transport: ISO 8601 (
toISOString()) oder Millisekunden/Unix-Sekunden. - UI/Anzeige:
Intl.DateTimeFormatmit explizitertimeZone. - Parsing: Nur ISO 8601 verwenden. Keine sprach- oder regionenspezifischen Formate parsen.
- Arithmetik: Für kalendarische Berechnungen UTC-normalisieren; für “Dauern” Millisekunden verwenden.
- Monate: Zero-based im Konstruktor und bei
getMonth()/setMonth(). - Wochentag:
getDay()gibt 0–6 mit Sonntag=0; nicht mitgetDate()verwechseln. - Messungen:
performance.now()für Benchmarks nutzen. - Zeitzone bewusst setzen: UI-Ausgabe immer mit expliziter IANA-Zeitzone, wenn erwartbar.
Kochrezepte für den Alltag
Start/Ende des Tages
function startOfDayLocal(d) {
const c = new Date(d.getTime());
c.setHours(0, 0, 0, 0);
return c;
}
function endOfDayLocal(d) {
const c = new Date(d.getTime());
c.setHours(23, 59, 59, 999);
return c;
}
Nächster Montag
function nextMonday(d) {
const c = new Date(d.getTime());
const day = c.getDay(); // So=0, Mo=1, ...
const delta = (8 - day) % 7 || 7; // Wenn heute Mo, dann +7 Tage
c.setDate(c.getDate() + delta);
return c;
}
Runden auf 5-Minuten
function roundTo5Min(d) {
const ms = 5 * 60 * 1000;
return new Date(Math.round(d.getTime() / ms) * ms);
}
Kalenderwoche (ISO-8601)
// ISO-Woche: Montag als Wochenbeginn, KW 1 enthält den 4. Januar
function getISOWeek(date) {
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const dayNum = d.getUTCDay() || 7; // So=7
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
const weekNo = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
return { year: d.getUTCFullYear(), week: weekNo };
}
Stabil in bestimmter Zeitzone formatieren
function formatInTZ(d, timeZone, locale = 'de-DE') {
return new Intl.DateTimeFormat(locale, {
timeZone,
dateStyle: 'medium',
timeStyle: 'short'
}).format(d);
}
Alternativen und Erweiterungen: Intl, Temporal, Libraries
Intl.DateTimeFormat ist die native, performante Lösung für Formatierung und Lokalisierung, inklusive IANA-Zeitzonen. Wenn du komplexere Logik brauchst, helfen Libraries:
- date-fns: Moderne, treeshakable Funktionen für Parsing, Formatierung, Arithmetik. Gute Wahl für modulare Nutzung.
- Luxon: Auf Intl basierend, mit DateTime-Objekt und robuster Zeitzonen-Unterstützung.
- Moment.js (Legacy): Stabil, aber “maintenance mode”. Für neue Projekte nicht mehr empfohlen.
Die Temporal-API ist eine in Entwicklung befindliche Spezifikation, die Datums-/Zeitlogik sicherer und expliziter machen soll (z. B. Temporal.ZonedDateTime, Temporal.PlainDate). In einigen Umgebungen ist sie bereits als Polyfill nutzbar. Für produktive Browser-/Node-Umgebungen prüfe den aktuellen Support-Status und verwende bei Bedarf das Polyfill.
Fehlerquellen und Debugging
- “YYYY-MM-DD” als UTC: Wenn du lokale Mitternacht brauchst, setze Zeit und Zeitzone explizit oder konstruiere numerisch mit lokalen Komponenten.
- Sommerzeit-Sprünge: Zeitspannen immer über Millisekunden und ggf. in UTC rechnen; Für “Kalendertage” normalisieren.
- Monatsindex 0–11: Häufigster Bug bei
new Date(year, monthIndex, ...). - Parsing inhomogener Strings: Nur ISO akzeptieren, alles andere vorher selbst validieren/konvertieren.
- Invalid Date: Immer über
isNaN(d.getTime())prüfen.
Leistungsaspekte
- Formatierung: Erzeuge ein
Intl.DateTimeFormat-Objekt und wiederverwende es, wenn viele Datumswerte formatiert werden. - Aktuellen Zeitwert mit
Date.now()stattnew Date().getTime()ermitteln – minimal schneller und ohne Objekt-Allokation. - Benchmarks/Timer:
performance.now()nutzen, da eine monotone Uhr benötigt wird – bedenke, dass der Event Loop dafür sorgt, dass alle zeitkritischen Aufgaben reibungslos abgearbeitet werden.
Praxisnahe Beispiele integriert
1) Fälligkeitsdatum in 30 Kalendertagen (lokal) mit Anzeige in Berlin-Zeit
function dueIn30Days() {
const now = new Date();
const due = new Date(now.getTime());
due.setDate(due.getDate() + 30);
const fmt = new Intl.DateTimeFormat('de-DE', {
dateStyle: 'full',
timeStyle: 'short',
timeZone: 'Europe/Berlin'
});
return fmt.format(due);
}
2) API-Eingang als ISO speichern, UI lokal anzeigen
// Server sendet ISO (UTC):
const iso = '2024-01-31T23:59:00.000Z';
const d = new Date(iso);
// UI (lokal):
const ui = new Intl.DateTimeFormat('de-DE', { dateStyle: 'medium', timeStyle: 'short' }).format(d);
// Persistenz:
const persist = d.toISOString();
3) Dauer in Stunden/Minuten robust zeigen
function formatDuration(ms) {
const totalMin = Math.floor(ms / 60000);
const h = Math.floor(totalMin / 60);
const m = totalMin % 60;
return `${h}h ${String(m).padStart(2, '0')}m`;
}
const started = Date.now();
// ... Arbeit ...
const elapsed = Date.now() - started;
console.log(formatDuration(elapsed));
Fazit
JavaScript Date ist ein leistungsfähiges Fundament für Datums- und Zeitlogik – wenn du seine Eigenheiten bewusst einsetzt. Arbeite für Transport und Speicherung mit ISO-Strings oder numerischen Timestamps, formatiere für die UI mit Intl.DateTimeFormat und setze Zeitzonen explizit, wenn du eine bestimmte Region erwartest. Nutze das Rollover-Verhalten für Arithmetik, normalisiere für kalendarische Differenzen auf UTC-Mitternacht und meide uneinheitliche String-Formate. Für komplexere Anforderungen stehen dir moderne Libraries und die aufkommende Temporal-API zur Verfügung. Mit diesen Prinzipien baust du robuste und vorhersagbare Datumsfunktionen – von der API-Schicht bis zur sauberen Anzeige.
FAQ
Wie konvertiere ich zwischen Date und Unix-Timestamp?
Date liefert Millisekunden. Unix-Timestamps sind Sekunden.
- Date → Unix:
Math.floor(Date.now() / 1000) - Unix → Date:
new Date(unixSeconds * 1000)
Warum ist “2024-01-01” nicht lokale Mitternacht?
Das ISO-Datum ohne Zeit/Offset wird in modernen Engines als UTC interpretiert. Wenn du lokale Mitternacht willst, verwende numerische Konstruktion new Date(year, monthIndex, day) oder setze explizit Stunde/Zeitzone in der Formatierung.
Wieso beginnt getMonth() bei 0?
Das ist Designhistorie der Spezifikation. Januar=0, Dezember=11. Achte beim numerischen Konstruktor und bei getMonth()/setMonth() stets auf Zero-based Monate.
Worin liegt der Unterschied zwischen getDate() und getDay()?
getDate() liefert den Tag im Monat (1–31). getDay() liefert den Wochentag (0–6, Sonntag=0). Diese Verwechslung ist ein häufiger Bug.
Wie formatiere ich zuverlässig in einer bestimmten Zeitzone?
Mit Intl.DateTimeFormat und timeZone:
new Intl.DateTimeFormat('de-DE', {
dateStyle: 'medium',
timeStyle: 'short',
timeZone: 'Europe/Berlin'
}).format(date);
Wie prüfe ich auf Invalid Date?
Über die Zeitwert-Prüfung:
const d = new Date(input);
if (isNaN(d.getTime())) {
// ungültig
}
Wie berechne ich kalendarische Tagesdifferenzen ohne DST-Fehler?
Normalisiere beide Daten auf UTC-Mitternacht und teile die Differenz durch 86400000. Beispiel siehe diffCalendarDays oben.
Ist Date.now() oder new Date() besser?
Date.now() ist minimal schneller und erzeugt kein Objekt. Wenn du nur den Zeitstempel brauchst, nimm Date.now().
Wann sollte ich Libraries nutzen?
Wenn du viele Operationen auf Kalenderbasis, wiederkehrende Formatierungen in vielen Locales, komplexe Zeitzonenlogik oder saubere, deklarative APIs brauchst. date-fns und Luxon sind gute moderne Optionen.