Definition
- DateTimeFormatter.ofPattern(pattern) creates a formatter from a pattern string.
- dd = 2-digit day, MMM = 3-letter month abbreviation, yyyy = 4-digit year.
- Thread-safe: DateTimeFormatter is immutable and safe to share as a
static finalconstant. - Locale: use
withLocale(Locale.ENGLISH)to ensure English month names. - format() converts
LocalDatetoString; parse() does the reverse.
Priya at the National Bank of India generates a staff report where each joining date must appear as '15-Jan-2021' instead of '2021-01-15'. The formatter is like a stamp template: every date pressed through it comes out in the same human-readable format.
Understanding the Concepts
What does this question ask?
The interviewer wants you to format dates using the java.time API. This tests DateTimeFormatter pattern syntax, locale awareness, and stream integration.
How do you format joining dates in a stream?
package com.bank.hr;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.Locale;
public class FormatJoinDate {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("dd-MMM-yyyy", Locale.ENGLISH);
public static void main(String[] args) {
List<Employee> employees = Employee.getSampleEmployees();
employees.stream()
.sorted(Comparator.comparing(Employee::getJoinDate))
.forEach(e -> System.out.println(
e.getName() + ": " + e.getJoinDate().format(FORMATTER)));
}
}
Suresh: 01-Apr-2005
Rajesh: 01-Mar-2015
Amit: 10-Sep-2018
Kavita: 01-Jul-2019
Priya: 01-Jun-2021
Neha: 15-Mar-2022
Ananya: 10-Jan-2023
Meera: 20-Jul-2024
What do the pattern letters mean?
| Pattern | Meaning | Example |
|---|---|---|
| dd | Day of month (2 digits, zero-padded) | 01, 15, 31 |
| d | Day of month (1-2 digits) | 1, 15, 31 |
| MMM | Month abbreviation (3 letters) | Jan, Feb, Dec |
| MM | Month number (2 digits) | 01, 06, 12 |
| MMMM | Full month name | January, June |
| yyyy | Year (4 digits) | 2021, 2024 |
| yy | Year (2 digits) | 21, 24 |
| EEEE | Full day name | Monday, Friday |
| EEE | Day abbreviation | Mon, Fri |
// Different patterns
LocalDate date = LocalDate.of(2021, 6, 15);
System.out.println(date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
// 15/06/2021
System.out.println(date.format(DateTimeFormatter.ofPattern("MMMM dd, yyyy", Locale.ENGLISH)));
// June 15, 2021
System.out.println(date.format(DateTimeFormatter.ofPattern("EEE, dd MMM yyyy", Locale.ENGLISH)));
// Tue, 15 Jun 2021
Why is Locale important?
Without specifying a locale, MMM uses the system default locale, which could produce month names in Hindi, Japanese, or any other language.
// WRONG: depends on system locale
DateTimeFormatter wrong = DateTimeFormatter.ofPattern("dd-MMM-yyyy");
// On a Hindi system: 15-जून-2021
// CORRECT: explicit English locale
DateTimeFormatter correct = DateTimeFormatter.ofPattern(
"dd-MMM-yyyy", Locale.ENGLISH);
// Always: 15-Jun-2021
What are common mistakes?
Mistake 1: using mm instead of MM for months.
// WRONG: mm is minutes, not months!
DateTimeFormatter wrong = DateTimeFormatter.ofPattern("dd-mm-yyyy");
// Throws DateTimeException: "Field MinuteOfHour cannot be printed"
// CORRECT: MM is months
DateTimeFormatter correct = DateTimeFormatter.ofPattern("dd-MM-yyyy");
Mistake 2: creating a new formatter inside the stream for every element.
// WRONG: creates a new formatter for every employee
employees.stream()
.map(e -> e.getJoinDate().format(
DateTimeFormatter.ofPattern("dd-MMM-yyyy"))) // new formatter each time!
.toList();
// CORRECT: reuse a static final formatter
private static final DateTimeFormatter FMT =
DateTimeFormatter.ofPattern("dd-MMM-yyyy", Locale.ENGLISH);
employees.stream()
.map(e -> e.getJoinDate().format(FMT))
.toList();
How do you parse a formatted string back to LocalDate?
String dateStr = "15-Jun-2021";
LocalDate parsed = LocalDate.parse(dateStr,
DateTimeFormatter.ofPattern("dd-MMM-yyyy", Locale.ENGLISH));
System.out.println(parsed); // 2021-06-15
How do you build a formatted report with streams?
String report = employees.stream()
.sorted(Comparator.comparing(Employee::getJoinDate))
.map(e -> String.format("%-10s %-12s %s",
e.getName(),
e.getDepartment(),
e.getJoinDate().format(FORMATTER)))
.collect(Collectors.joining("\n"));
System.out.println(report);
What are common interview follow-up questions?
- "How do you format with time?" Use
DateTimeFormatter.ofPattern("dd-MMM-yyyy HH:mm:ss")withLocalDateTime. - "Is DateTimeFormatter thread-safe?" Yes, it is immutable and safe for concurrent use.
- "How do you handle invalid date strings during parse?" Catch
DateTimeParseException.
Visual Overview
LocalDate: 2021-06-15
│
│ .format(DateTimeFormatter.ofPattern("dd-MMM-yyyy"))
▼
Pattern parsing:
┌──────────────────────────────────┐
│ dd → 15 (day, zero-padded) │
│ - → - (literal) │
│ MMM → Jun (month abbreviation)│
│ - → - (literal) │
│ yyyy → 2021 (4-digit year) │
└──────────────────────────────────┘
│
▼
String: "15-Jun-2021"
Key Takeaway
Formatting dates uses DateTimeFormatter.ofPattern with pattern letters: dd for day, MMM for month abbreviation, yyyy for year. The expression date.format(DateTimeFormatter.ofPattern("dd-MMM-yyyy", Locale.ENGLISH)) produces strings like "15-Jun-2021".
Always specify Locale.ENGLISH explicitly. Without it, MMM uses the system locale, which could produce month names in Hindi, Japanese, or any other language. Store the formatter as a static final constant because it is immutable and thread-safe.
The most common mistake is using lowercase mm (minutes) instead of uppercase MM (months). The formatter is case-sensitive: mm throws DateTimeException when applied to a LocalDate because LocalDate has no minute field.
Remember one sentence: DateTimeFormatter.ofPattern with dd-MMM-yyyy and an explicit Locale formats dates consistently, and always store the formatter as a static final constant.
---