Formatting and Parsing Dates and Times with DateTimeFormatter in Java

Illustration for Formatting and Parsing Dates and Times with DateTimeFormatter in Java
By Last updated:

Every global application—from banking systems to scheduling platforms—needs to display and interpret dates in multiple formats. A common pain point developers face is parsing dates from user input or formatting dates for APIs and logs. Using old utilities like SimpleDateFormat introduces thread-safety risks and bugs.

The modern solution is java.time.format.DateTimeFormatter, a thread-safe, immutable, and flexible API for both formatting and parsing.


1. Formatting Dates with Predefined Formatters

LocalDate date = LocalDate.now();
System.out.println(date.format(DateTimeFormatter.ISO_DATE)); 
// 2025-08-28

LocalDateTime dateTime = LocalDateTime.now();
System.out.println(dateTime.format(DateTimeFormatter.ISO_DATE_TIME)); 
// 2025-08-28T14:55:30.456

✅ Use predefined constants (ISO_DATE, ISO_DATE_TIME, RFC_1123_DATE_TIME) for standard compliance.


2. Custom Patterns

LocalDate date = LocalDate.of(2025, 8, 28);
DateTimeFormatter custom = DateTimeFormatter.ofPattern("dd/MM/yyyy");
System.out.println(date.format(custom)); // 28/08/2025

✅ Useful for user interfaces and reports.
❌ Without locale, patterns may behave differently in various regions.

DateTimeFormatter german = DateTimeFormatter.ofPattern("dd.MM.yyyy", Locale.GERMANY);
System.out.println(date.format(german)); // 28.08.2025

3. Parsing Strings into Dates

String input = "28-08-2025 15:30";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");
LocalDateTime parsed = LocalDateTime.parse(input, formatter);
System.out.println("Parsed DateTime: " + parsed);

✅ Parsing must match the exact pattern.


4. Locale-Specific Formatting

LocalDate today = LocalDate.now();

DateTimeFormatter french = DateTimeFormatter.ofPattern("d MMMM yyyy", Locale.FRENCH);
System.out.println(today.format(french)); // 28 août 2025

✅ Enables true internationalization.


5. Flexible Parsing with DateTimeFormatterBuilder

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .appendOptional(DateTimeFormatter.ofPattern("dd-MM-yyyy"))
    .appendOptional(DateTimeFormatter.ofPattern("yyyy/MM/dd"))
    .toFormatter();

LocalDate date1 = LocalDate.parse("28-08-2025", formatter);
LocalDate date2 = LocalDate.parse("2025/08/28", formatter);

System.out.println(date1 + " | " + date2);

✅ Supports multiple input formats.


6. Common Pitfalls and Anti-Patterns

  • ❌ Using SimpleDateFormat → not thread-safe.
  • ❌ Forgetting to specify locale → breaks parsing/formatting in non-default locales.
  • ❌ Mismatching patterns (MM for months vs mm for minutes).
  • ❌ Ignoring time zones when parsing ZonedDateTime.

7. Best Practices

  • ✅ Use predefined ISO/RFC constants for APIs and protocols.
  • ✅ For UI, apply localized patterns with Locale.
  • ✅ Use DateTimeFormatterBuilder for flexible parsing.
  • ✅ Keep formatters as constants—immutable and thread-safe.

📌 What's New in Java Versions?

  • Java 8: Introduced DateTimeFormatter.
  • Java 11: Enhanced localized patterns.
  • Java 17: Stable, no major changes.
  • Java 21: Ongoing locale and zone updates.

✅ The API has been reliable since Java 8.


Real-World Analogy

Think of DateTimeFormatter as a translator:

  • ISO format is the universal language.
  • Custom formats are local dialects.
  • Without the right dialect (locale), your “message” can be misunderstood.

Conclusion + Key Takeaways

  • ❌ Avoid SimpleDateFormat.
  • ✅ Use DateTimeFormatter for thread-safe, immutable formatting/parsing.
  • ✅ Leverage ISO for APIs, RFC for protocols, and localized patterns for UI.
  • ✅ Always specify locales in custom patterns.

Correct usage ensures clarity, reliability, and compatibility in global systems.


FAQ: Expert-Level Q&A

1. Is DateTimeFormatter thread-safe?
Yes—safe for concurrent use.

2. What’s the difference between ofPattern() and predefined formatters?
Predefined are ISO/RFC-compliant; ofPattern() supports custom needs.

3. Can I use multiple patterns for parsing?
Yes, via DateTimeFormatterBuilder.

4. What happens if the input string doesn’t match the pattern?
DateTimeParseException is thrown.

5. Can DateTimeFormatter handle time zones?
Yes, with ZonedDateTime and OffsetDateTime.

6. How to format dates in a user’s preferred language?
Use Locale in the formatter.

7. Does DateTimeFormatter support week-based years?
Yes, using YYYY instead of yyyy.

8. What’s the performance impact of creating formatters repeatedly?
Minimal, but reuse constants for efficiency.

9. How to format milliseconds and nanoseconds?
Use SSS for milliseconds, nnnnnnnnn for nanoseconds.

10. Can I combine literals in patterns?
Yes, enclose them in single quotes ('at'). Example: "yyyy-MM-dd 'at' HH:mm".