W3docs

Java Datumsformatierung

Datum und Uhrzeit in Java mit DateTimeFormatter und Standard- oder benutzerdefinierten Mustern in Strings umwandeln.

Datumsformatierung ist die Umwandlung eines Datums-/Uhrzeitwerts in einen für Menschen lesbaren String. Dieses Kapitel behandelt DateTimeFormatter — wie man einen erstellt (eingebaut, lokalisiert oder musterbasiert), das vollständige Muster-Alphabet, die Handhabung von Locale und Zeitzonen sowie die häufigsten Fehlerquellen, die zu falscher Ausgabe führen. Es funktioniert mit jedem java.time-Typ: LocalDate, LocalTime, LocalDateTime, ZonedDateTime und Instant.

Jeder java.time-Typ hat eine toString()-Methode, die die ISO-8601-Darstellung erzeugt: 2025-11-04, 14:30:00, 2025-11-04T14:30:00Z. Das ist für Logs und maschinelle Kommunikation ausreichend. Für die Anzeige für Menschen ("4. November 2025" oder "4. Nov, 14:30") benötigt man einen Formatter.

Die Klasse heißt java.time.format.DateTimeFormatter. Sie ist der moderne, thread-sichere und unveränderliche Ersatz für das veraltete java.text.SimpleDateFormat (das nicht thread-sicher war und bei gemeinsamer Verwendung zu schwer nachvollziehbaren Produktionsfehlern führte). Verwende eine Instanz als static final und nutze sie dauerhaft in allen Threads — keine Synchronisation, kein defensives Kopieren nötig.

Drei Wege, einen Formatter zu erstellen

// 1. Built-in ISO formatters
DateTimeFormatter.ISO_LOCAL_DATE;                  // 2025-11-04
DateTimeFormatter.ISO_LOCAL_DATE_TIME;             // 2025-11-04T14:30:00
DateTimeFormatter.ISO_OFFSET_DATE_TIME;            // 2025-11-04T14:30:00-05:00
DateTimeFormatter.ISO_ZONED_DATE_TIME;             // 2025-11-04T14:30:00-05:00[America/New_York]
DateTimeFormatter.ISO_INSTANT;                     // 2025-11-04T19:30:00Z
DateTimeFormatter.BASIC_ISO_DATE;                  // 20251104

// 2. Localised formatters
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);          // November 4, 2025 (en-US)
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);    // Nov 4, 2025, 2:30:00 PM

// 3. Pattern-based formatters
DateTimeFormatter.ofPattern("dd MMM yyyy");                   // 04 Nov 2025
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm zzz");          // 2025-11-04 14:30 EST

Die Muster-API wird am häufigsten verwendet. Die lokalisierte Variante ist die richtige Wahl, wenn ein kulturell angemessenes Format gewünscht wird und das JDK das Layout selbst wählen soll.

Formatierung

Der Aufruf ist auf beiden Seiten symmetrisch:

String s = formatter.format(temporal);
String s2 = temporal.format(formatter);                       // same thing, fluent style

Beide Varianten funktionieren. Die meisten Codebasen verwenden die Fluent-Form.

LocalDate today = LocalDate.now();
String us = today.format(DateTimeFormatter.ofPattern("MM/dd/yyyy"));         // 11/04/2025
String iso = today.format(DateTimeFormatter.ISO_LOCAL_DATE);                 // 2025-11-04
String eu = today.format(DateTimeFormatter.ofPattern("dd.MM.yyyy"));          // 04.11.2025

Das Muster-Alphabet

Die große Übersichtstabelle — die man immer wieder nachschlagen wird. Buchstaben sind case-sensitiv, und die Anzahl der Zeichen ist entscheidend.

BuchstabeBedeutungBeispiel
yJahry2025, yy25, yyyy2025
MMonatM11, MM11, MMMNov, MMMMNovember
dTag des Monatsd4, dd04
EWochentagETue, EEEETuesday
HStunde 0-23H14, HH14
hStunde 1-12h2, hh02 (zusammen mit a verwenden)
aAM/PMaPM
mMinutem5, mm05
sSekundes9, ss09
SSekundenbruchteileSSS123 (Millisekunden)
nNanosekundennnnnnnnn123456789
zZonennamezEST, zzzzEastern Standard Time
ZZonenversatzZ-0500, ZZ-0500, ZZZZGMT-05:00
XISO-VersatzX-05, XX-0500, XXX-05:00
VZonen-IDVVAmerica/New_York

Literaltext wird in einfache Anführungszeichen eingeschlossen:

DateTimeFormatter.ofPattern("EEEE, MMMM d 'at' h:mm a");      // Tuesday, November 4 at 2:30 PM

Für ein wörtliches einfaches Anführungszeichen verwendet man zwei: ''.

Das am häufigsten verwechselte Paar ist m vs. M (Kleinbuchstabe = Minute, Großbuchstabe = Monat) und H vs. h (Großbuchstabe = 0-23, Kleinbuchstabe = 1-12). Die meisten "die Uhrzeit ist irgendwie falsch"-Fehler haben ihren Ursprung in einem dieser Tippfehler.

Lokalisierung: Locale und withLocale

Ein Formatter verwendet die Standard-Locale der JVM, sofern nicht anders angegeben. Für eine Ausgabe "immer auf Englisch" oder "immer auf Deutsch" sollte die Locale explizit festgelegt werden:

DateTimeFormatter english = DateTimeFormatter.ofPattern("EEEE, MMMM d", Locale.US);
DateTimeFormatter german  = DateTimeFormatter.ofPattern("EEEE, d. MMMM", Locale.GERMAN);
DateTimeFormatter french  = DateTimeFormatter.ofPattern("EEEE d MMMM", Locale.FRENCH);

today.format(english);   // Tuesday, November 4
today.format(german);    // Dienstag, 4. November
today.format(french);    // mardi 4 novembre

Für serverseitig gerendertem Inhalt sollte immer eine Locale übergeben werden. Die "Standard-Locale der JVM" ist auf Produktionsservern nicht vorhersehbar und die Ursache für "funktioniert auf meinem Laptop, nicht aber auf dem Server"-Fehler.

Zeitzonendarstellung

ZonedDateTime und Instant sind die einzigen Typen, die Zeitzoneninformationen enthalten. Das Formatieren eines LocalDateTime mit einem Muster, das z oder Z enthält, führt zu einer Ausnahme — es gibt keine Zeitzone zum Ausgeben. Zuerst konvertieren:

ZonedDateTime zdt = ldt.atZone(ZoneId.of("America/New_York"));
zdt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z"));   // 2025-11-04 14:30 EST

Auch für Instant benötigt der Formatter eine Zeitzone — Instant hat keine eigene, daher brauchen Formatter, die Zeitzonen-abhängige Felder enthalten, ein withZone:

DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
    .withZone(ZoneId.of("America/New_York"));
f.format(Instant.now());                                      // formatter supplies the zone for display

Ohne withZone wirft das Formatieren eines Instant mit einem kalenderförmigen Muster eine Ausnahme.

Formatierte Formatter mit FormatStyle

Die lokalisierten Factories bieten vier kanonische Größen:

DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);   // 11/4/25 (en-US), 04.11.25 (de-DE)
DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);  // Nov 4, 2025
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);    // November 4, 2025
DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);    // Tuesday, November 4, 2025

Dieselben vier Größen gibt es für ofLocalizedTime und ofLocalizedDateTime. Verwende diese, wenn das Layout der Locale des Benutzers folgen soll, anstatt ein festes Format zu erzwingen. Kombiniere sie mit .withLocale(...), um die Locale festzulegen.

Ein praktisches Beispiel: ein Datum, sechs Anzeigevarianten

Das folgende Programm formatiert ein ZonedDateTime auf sechs gängige Arten: ISO für maschinelle Logs, US-12-Stunden-Format für englischsprachige Nutzer, europäisches 24-Stunden-Format für deutsche Nutzer, eine lange lokalisierte Form, ein benutzerdefiniertes Muster mit eingebettetem Literaltext sowie ein Instant-via-withZone-Formatter für rohe Zeitstempel.

java— editable, runs on the server

Was man der Ausgabe entnehmen kann:

  • Die gecachten static final DateTimeFormatter-Felder sind die richtige Vorgehensweise. DateTimeFormatter ist unveränderlich und thread-sicher; das Erstellen einer Instanz ist günstig, aber nicht kostenlos, und die Wiederverwendung derselben Instanz überall ist das vom JDK empfohlene Muster. Innerhalb einer häufig aufgerufenen Schleife sollte keine neue Instanz erstellt werden.
  • Dasselbe ZonedDateTime erzeugte sechs verschiedene Strings — abhängig vom Formatter. Das Wertobjekt änderte sich nie; der Formatter ist das einzige, was das Layout steuert. Das ist die Trennung, für die DateTimeFormatter existiert — den Werttyp sauber halten und die Darstellung dem Formatter überlassen.
  • Der "häufige Tippfehler"-Block gab 14:11 für HH:MM aus, weil M Monat bedeutet, nicht Minute. Diese beiden sind das am häufigsten verwechselte Paar im Muster-Alphabet. Wenn eine angezeigte Uhrzeit verdächtig nach einem Datumskomponenten aussieht, sollte man die Groß-/Kleinschreibung im Muster prüfen.
  • Die FormatStyle-Leiter erzeugte vier zunehmend längere Strings. FormatStyle.MEDIUM ist ein sinnvoller Standard für "Datum anzeigen, ohne zu viel nachzudenken"; LONG und FULL für Kontexte, in denen Jahr und Wochentag eindeutig sein müssen; SHORT für enge UI-Bereiche.
  • LocalDateTime mit einem zonenabhängigen Muster warf eine Ausnahme — der Formatter benötigt Zonendaten, und LocalDateTime hat keine. Die Lösung ist eine Konvertierung (ldt.atZone(zone)) oder das Entfernen des zonenabhängigen Felds aus dem Muster. In jedem Fall ist das Fehlverhalten zur Laufzeit klar erkennbar.

Wie geht es weiter

Die Formatierung ist die Richtung Wert → String. Das nächste Kapitel, Java Date Parsing, ist das Gegenteil — String → Wert — unter Verwendung derselben DateTimeFormatter-Muster und derselben Einschränkungen. Beide zusammen bilden die I/O-Grenze für jeden Code, der Datumsangaben mit Benutzern, Konfigurationen, Logs oder externen APIs austauscht.

Übungen

Übung
Ein Webserver protokolliert Zeitstempel mit `ZonedDateTime.now().format(DateTimeFormatter.ofPattern('yyyy-MM-dd HH:mm'))`. Auf einer JVM mit deutscher Locale wird der Monat als 'Nov' anstatt als '11' ausgegeben. Was ist die wahrscheinlichste Ursache?
Ein Webserver protokolliert Zeitstempel mit `ZonedDateTime.now().format(DateTimeFormatter.ofPattern('yyyy-MM-dd HH:mm'))`. Auf einer JVM mit deutscher Locale wird der Monat als 'Nov' anstatt als '11' ausgegeben. Was ist die wahrscheinlichste Ursache?
Was this page helpful?