Java DateTimeFormatter Playground

Presets:
Java snippet

What is DateTimeFormatter?

java.time.format.DateTimeFormatter is the modern Java class for formatting and parsing date/time values. Introduced in Java 8 as part of JSR-310 (the java.time package), it replaces the old SimpleDateFormat from java.text.

Three things make it superior to the legacy API: it is immutable and therefore thread-safe, it integrates with the full java.time type hierarchy (LocalDate, LocalDateTime, ZonedDateTime, Instant, OffsetDateTime), and it supports a much richer set of pattern letters.

Pattern letters reference

A pattern is a string of letters where each letter (and its repetition count) maps to a date-time component. Non-letter characters appear verbatim; letters wrapped in single quotes are treated as literals.

LetterMeaningExample
yYear of erayyyy β†’ 2026
uProleptic year (can be negative)uuuu β†’ 2026
M / LMonth (M = formatting, L = standalone)M β†’ 4, MM β†’ 04, MMM β†’ Apr, MMMM β†’ April
dDay of monthdd β†’ 07
DDay of yearDDD β†’ 097
EDay of week (text)E β†’ Tue, EEEE β†’ Tuesday
eDay of week (number, locale-sensitive)e β†’ 2
aAM/PM markera β†’ PM
HHour of day (0–23)HH β†’ 14
kHour of day (1–24)kk β†’ 24
hClock hour (1–12)hh β†’ 02
KClock hour (0–11)KK β†’ 02
mMinute of hourmm β†’ 30
sSecond of minutess β†’ 45
SFraction of secondSSS β†’ 123 (milliseconds)
nNano of secondn β†’ 123000000
VTime zone IDVV β†’ Europe/Paris
zTime zone namez β†’ CEST, zzzz β†’ Central European Summer Time
ZZone offset, no colonZ β†’ +0200
XISO zone offset, Z for UTCX β†’ +02, XX β†’ +0200, XXX β†’ +02:00
xISO zone offset, +0000 for UTCxxx β†’ +02:00
'text'Literal text'T' β†’ T
''Escaped single quote'' β†’ '

Letters A-Z and a-z are reserved for future use. Avoid using letters not listed above as literals unless quoted.

Predefined formatters

DateTimeFormatter ships with constants for the most common ISO-8601 formats:

ConstantPatternExample
ISO_LOCAL_DATEyyyy-MM-dd2026-04-20
ISO_LOCAL_TIMEHH:mm:ss14:30:45
ISO_LOCAL_DATE_TIMEyyyy-MM-dd'T'HH:mm:ss2026-04-20T14:30:45
ISO_OFFSET_DATE_TIMEyyyy-MM-dd'T'HH:mm:ssXXX2026-04-20T14:30:45+02:00
ISO_ZONED_DATE_TIMEoffset + [zone]2026-04-20T14:30:45+02:00[Europe/Paris]
ISO_INSTANTUTC Z form2026-04-20T12:30:45Z
RFC_1123_DATE_TIMEHTTP / emailMon, 20 Apr 2026 14:30:45 +0200
BASIC_ISO_DATEyyyyMMdd20260420

Prefer a predefined formatter over a hand-written pattern whenever one fits β€” they're battle-tested and make intent obvious to readers.

Using DateTimeFormatter in code

Formatting

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

// Simple case
LocalDateTime now = LocalDateTime.now();
String s = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);

// Custom pattern
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm");
String s2 = now.format(fmt);   // "20 Apr 2026 14:30"

// Locale + zone
ZonedDateTime paris = ZonedDateTime.now(ZoneId.of("Europe/Paris"));
DateTimeFormatter french = DateTimeFormatter
    .ofPattern("EEEE d MMMM yyyy 'Γ ' HH:mm", Locale.FRENCH);
String s3 = paris.format(french);   // "lundi 20 avril 2026 Γ  14:30"

Parsing

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate d = LocalDate.parse("20/04/2026", fmt);

// Parse an ISO offset string
OffsetDateTime odt = OffsetDateTime.parse("2026-04-20T14:30:45+02:00");

// Parse with a specific zone
ZonedDateTime zdt = ZonedDateTime.parse("2026-04-20T14:30:45+02:00[Europe/Paris]");

Formatting an Instant

Instant has no zone, so you must provide one before formatting with fields that need a calendar:

Instant instant = Instant.now();

// This works β€” ISO_INSTANT is zone-agnostic
String iso = DateTimeFormatter.ISO_INSTANT.format(instant);

// This fails: ofPattern needs a zone for an Instant
DateTimeFormatter.ofPattern("yyyy-MM-dd").format(instant);  // throws

// Right way:
DateTimeFormatter.ofPattern("yyyy-MM-dd")
    .withZone(ZoneId.of("UTC"))
    .format(instant);

Locale-aware formatting

Text output β€” month names, weekday names, AM/PM markers β€” depends on the formatter's locale. If you don't set one, the formatter uses Locale.getDefault(), which is usually not what you want in a server application.

DateTimeFormatter us = DateTimeFormatter
    .ofPattern("MMMM d, yyyy", Locale.US);
DateTimeFormatter fr = DateTimeFormatter
    .ofPattern("d MMMM yyyy", Locale.FRANCE);
DateTimeFormatter de = DateTimeFormatter
    .ofPattern("d. MMMM yyyy", Locale.GERMANY);

LocalDate d = LocalDate.of(2026, 4, 20);
us.format(d);   // "April 20, 2026"
fr.format(d);   // "20 avril 2026"
de.format(d);   // "20. April 2026"

Rule of thumb for server code: always pass a Locale explicitly. Never rely on the JVM default β€” it can differ across Docker images, CI runners, and developer laptops.

Time zones and offsets

Three letters deal with zones: V (zone ID), z (zone name), and X / x / Z (offsets). They are not interchangeable:

  • VV β†’ Europe/Paris β€” the IANA zone ID, full canonical form
  • z β†’ CEST β€” the zone's short name at that moment (varies with DST)
  • zzzz β†’ Central European Summer Time β€” long name
  • Z β†’ +0200 β€” offset, no colon
  • XXX β†’ +02:00 β€” ISO offset with colon, Z for UTC
  • xxx β†’ +02:00 β€” like XXX but +00:00 for UTC (no Z)

For machine-readable output, prefer XXX (strictly ISO-8601). For logs read by humans, VV or z is friendlier.

DateTimeFormatter vs SimpleDateFormat

SimpleDateFormatDateTimeFormatter
Packagejava.textjava.time.format
Thread-safeNoYes (immutable)
Works withDate, CalendarAll java.time types
Nano-secondsNoYes (S up to 9 digits)
Zone handlingAmbiguous (z, Z)Clear distinction (V, z, X, x, Z)
Lenient by defaultYes (silent corruption)No (strict, throws)
Introduced inJDK 1.1 (1997)JDK 8 (2014)

If you see SimpleDateFormat in a code review, it's almost always worth migrating: a single shared SimpleDateFormat instance under load will produce garbage output eventually.

Common mistakes

  • Using YYYY instead of yyyy. Uppercase YYYY is the week-based year. On Dec 29-31 it can be off by one. Always use lowercase yyyy.
  • Using DD instead of dd. Uppercase DD is day-of-year (1–366). Lowercase dd is day-of-month.
  • Using mm for month. Minutes are lowercase m, months are uppercase M.
  • Sharing a SimpleDateFormat between threads. It's mutable and will corrupt output under concurrent access. DateTimeFormatter is immutable and safe to share.
  • Forgetting to quote a literal letter. yyyy-MM-dd T HH:mm will fail; use yyyy-MM-dd'T'HH:mm.
  • Formatting an Instant with fields that need a calendar. Instant has no zone β€” use .withZone(ZoneId.of("UTC")) or convert to ZonedDateTime first.
  • Assuming z is parseable round-trip. CEST is ambiguous (it's used in multiple zones in some datasets). Prefer VV for serialization.
  • Not setting a locale. "Apr" vs "avr." depends on the JVM default locale, which can vary across deployment targets.