DateTimeFormatter provides thread-safe formatting and parsing for all java.time types.

Basic Formatting

  LocalDate date = LocalDate.of(2024, 6, 15);
LocalDateTime dateTime = LocalDateTime.of(2024, 6, 15, 14, 30, 45);

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
System.out.println(date.format(formatter)); // 2024-06-15

DateTimeFormatter dateTimeFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(dateTime.format(dateTimeFmt)); // 2024-06-15 14:30:45
  

Predefined Formatters

  LocalDate date = LocalDate.now();

System.out.println(date.format(DateTimeFormatter.ISO_LOCAL_DATE));     // 2024-06-15
System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));     // 20240615

LocalDateTime dt = LocalDateTime.now();
System.out.println(dt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));  // 2024-06-15T14:30:45

Instant instant = Instant.now();
System.out.println(DateTimeFormatter.ISO_INSTANT.format(instant));      // 2024-06-15T06:30:45Z
  

Common Pattern Letters

Pattern Meaning Example
yyyy 4-digit year 2024
MM 2-digit month 06
MMM Abbreviated month Jun
MMMM Full month June
dd 2-digit day 15
EEE Abbreviated day Sat
HH Hour (0-23) 14
mm Minute 30
ss Second 45
SSS Millisecond 123
a AM/PM marker PM
z Timezone name CST
Z Timezone offset +0800
X ISO 8601 offset +08:00

Parsing

  DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy");

LocalDate date = LocalDate.parse("15/06/2024", fmt);
LocalDateTime dt = LocalDateTime.parse("15/06/2024 14:30",
    DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"));
  

Strict vs Lenient

  DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd")
    .withResolverStyle(ResolverStyle.STRICT);

// Throws exception for invalid dates like 2024-02-30
LocalDate.parse("2024-02-30", fmt);
  

Locale-Sensitive Formatting

  LocalDate date = LocalDate.of(2024, 6, 15);

DateTimeFormatter usFmt = DateTimeFormatter
    .ofPattern("MMMM dd, yyyy", Locale.US);
System.out.println(date.format(usFmt)); // June 15, 2024

DateTimeFormatter cnFmt = DateTimeFormatter
    .ofPattern("yyyy年MM月dd日", Locale.CHINA);
System.out.println(date.format(cnFmt)); // 2024年06月15日
  

Formatting ZonedDateTime

  ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));

DateTimeFormatter fmt = DateTimeFormatter.ofPattern(
    "yyyy-MM-dd HH:mm:ss z", Locale.ENGLISH);
System.out.println(zdt.format(fmt)); // 2024-06-15 14:30:00 CST
  

Reusable Formatter

DateTimeFormatter is immutable and thread-safe — create once, reuse everywhere:

  public class DateFormats {
    public static final DateTimeFormatter DATE =
        DateTimeFormatter.ofPattern("yyyy-MM-dd");
    public static final DateTimeFormatter DATETIME =
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    public static final DateTimeFormatter ISO =
        DateTimeFormatter.ISO_OFFSET_DATE_TIME;
}
  

Unlike SimpleDateFormat, no synchronization needed.

Best Practices

  • Store DateTimeFormatter as static constants — they are thread-safe
  • Use ISO format (ISO_LOCAL_DATE, ISO_INSTANT) for APIs and storage
  • Always specify a Locale for user-facing formatted output
  • Use ResolverStyle.STRICT when parsing user input
  • Prefer DateTimeFormatter over SimpleDateFormat in all new code