Date Formatting

Su Suriya Ravichandran Updated 14 Nov 2026
6 min read ·Lesson 118 of 133

Formatting converts a date or time value into text: 28/12/2026, Monday, 28 December 2026, 06:05 PM, or 2026-12-28T18:05:09+05:30. In java.time, formatting is done by java.time.format.DateTimeFormatter, which supports predefined standard formats, custom patterns, and locale-aware styles that follow each language's conventions automatically. Unlike the legacy SimpleDateFormat, DateTimeFormatter is immutable and thread-safe, so one instance can be shared across an entire application.

Formatting Basics

Every java.time type has a format method:

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

LocalDate date = LocalDate.of(2026, 12, 28);
String s = date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));   // "28/12/2026"

Equivalently, formatter.format(temporal). Calling toString() produces ISO-8601 text (2026-12-28), which is ideal for machines but not usually what users should see.

Predefined Formatters

DateTimeFormatter provides constants for standard formats:

ConstantExample output
ISO_LOCAL_DATE2026-12-28
ISO_LOCAL_TIME18:05:09
ISO_LOCAL_DATE_TIME2026-12-28T18:05:09
ISO_OFFSET_DATE_TIME2026-12-28T18:05:09+05:30
ISO_ZONED_DATE_TIME2026-12-28T18:05:09+05:30[Asia/Kolkata]
ISO_INSTANT2026-12-28T12:35:09Z
BASIC_ISO_DATE20261228
RFC_1123_DATE_TIMEMon, 28 Dec 2026 18:05:09 +0530 (HTTP headers, email)

Use the ISO formats for anything read by other programs: JSON APIs, log files, database exports, file names. They are unambiguous, sortable as text, and understood everywhere.

Custom Patterns

DateTimeFormatter.ofPattern(pattern) builds a formatter from letters that stand for date and time fields. The number of repeated letters controls the width or style.

Using 28 December 2026, 18:05:09 in Asia/Kolkata:

PatternOutputMeaning
dd/MM/yyyy28/12/2026Day, month, year
d MMM yyyy28 Dec 2026Abbreviated month name
EEEE, d MMMM yyyyMonday, 28 December 2026Full day and month names
HH:mm:ss18:05:0924-hour clock
hh:mm a06:05 PM12-hour clock with AM/PM
yyyy-MM-dd'T'HH:mm2026-12-28T18:05Literal text in single quotes
E, dd MMM yyyy HH:mm zMon, 28 Dec 2026 18:05 ISTZone abbreviation
xxx VV+05:30 Asia/KolkataOffset and zone ID
QQQ yyyyQ4 2026Quarter
D362Day of the year

Common pattern letters

LetterFieldExamples
yYearyy → 26, yyyy → 2026
MMonthM → 12, MM → 12, MMM → Dec, MMMM → December
dDay of monthd → 8, dd → 08
EDay of weekE → Mon, EEEE → Monday
HHour, 0–23HH → 18
hHour, 1–12hh → 06
aAM/PMPM
mMinutemm → 05
sSecondss → 09
SFraction of secondSSS → milliseconds
zZone nameIST, India Standard Time (zzzz)
VVZone IDAsia/Kolkata
x, X, ZOffset+05:30, +0530
'...'Literal text'T', 'at'

Pattern letters are case-sensitive

Several letters differ only by case, and swapping them produces output that looks plausible but is wrong:

WrongRightResult of the mistake
dd/mm/yyyydd/MM/yyyy28/05/2026: mm is minutes, not months
YYYY-MM-ddyyyy-MM-dd2027-12-28: YYYY is the week-based year
hh:mmHH:mm06:05 for 18:05 without an AM/PM marker
DDddDay of year, not day of month

The YYYY mistake is particularly dangerous: it gives the correct year for most of the year and fails only in the last days of December (or the first days of January), when the week containing the date belongs to the next (or previous) year. Systems have printed tickets and invoices with the wrong year because of this.

Formatting a type that lacks a field

A pattern can only use fields the value contains:

LocalDate.of(2026, 1, 1).format(DateTimeFormatter.ofPattern("HH:mm"));
// UnsupportedTemporalTypeException: Unsupported field: HourOfDay

A LocalDate has no hour; a LocalDateTime has no zone, so z and VV require a ZonedDateTime (or a formatter with .withZone(...)).

Locale-Aware Formatting

Month and day names, the order of day and month, separators, and the 12- or 24-hour convention all vary by language and country. ofLocalizedDate, ofLocalizedTime, and ofLocalizedDateTime produce a format appropriate to a locale:

DateTimeFormatter medium = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
DateTimeFormatter full = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);
LocaleMEDIUMFULL
en-USDec 28, 2026Monday, December 28, 2026
en-GB28 Dec 2026Monday, 28 December 2026
hi-IN28 दिस॰ 2026सोमवार, 28 दिसंबर 2026
ta-IN28 டிச., 2026திங்கள், 28 டிசம்பர், 2026
de-DE28.12.2026Montag, 28. Dezember 2026

The styles are SHORT, MEDIUM, LONG, and FULL. Use .withLocale(locale) to choose the language; without it, the JVM default locale is used. Custom patterns also use the locale for names: ofPattern("EEEE", Locale.GERMANY) produces Montag.

Prefer localized styles for user-facing text. A custom pattern such as MM/dd/yyyy imposes one country's convention on everyone, and 03/04/2026 means 4 March in the United States but 3 April in India and the UK.

Formatting Instants

An Instant has no time zone, so field-based patterns cannot format it directly. Attach a zone to the formatter:

DateTimeFormatter fileStamp = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss").withZone(ZoneOffset.UTC);
fileStamp.format(Instant.parse("2026-12-28T12:35:00Z"));     // "20261228_123500"

DateTimeFormatter.ISO_INSTANT formats an instant in UTC without any extra setup.

Reusing Formatters

DateTimeFormatter is immutable and thread-safe. Create each formatter once, typically as a static final constant, and reuse it everywhere:

private static final DateTimeFormatter DISPLAY = DateTimeFormatter.ofPattern("dd MMM yyyy, hh:mm a", Locale.ENGLISH);

This is a major improvement over SimpleDateFormat, which is mutable and produced corrupted output when one instance was shared between threads.

Practical Example: Exam Reminders for International Students

An online exam starts at a single moment. Each student should see the time in their own zone and language, while logs and file names use standard, machine-friendly formats.

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

public class Notify {
    record User(String name, ZoneId zone, Locale locale) { }

    private static final DateTimeFormatter LOG = DateTimeFormatter.ISO_INSTANT;
    private static final DateTimeFormatter FILE = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss").withZone(ZoneOffset.UTC);

    static String message(User u, Instant when) {
        ZonedDateTime local = when.atZone(u.zone());
        DateTimeFormatter date = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(u.locale());
        DateTimeFormatter time = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withLocale(u.locale());
        return u.name() + ": " + local.format(date) + ", " + local.format(time) + " (" + u.zone().getId() + ")";
    }

    public static void main(String[] args) {
        Instant examStart = Instant.parse("2026-12-28T12:35:00Z");
        List<User> users = List.of(
            new User("Ada", ZoneId.of("Europe/London"), Locale.UK),
            new User("Ravi", ZoneId.of("Asia/Kolkata"), Locale.forLanguageTag("en-IN")),
            new User("Meena", ZoneId.of("Asia/Kolkata"), Locale.forLanguageTag("ta-IN")),
            new User("Lukas", ZoneId.of("Europe/Berlin"), Locale.GERMANY),
            new User("Grace", ZoneId.of("America/New_York"), Locale.US)
        );
        for (User u : users) System.out.println(message(u, examStart));
        System.out.println("Log:  " + LOG.format(examStart));
        System.out.println("File: report_" + FILE.format(examStart) + ".pdf");
    }
}

Output:

Ada: Monday, 28 December 2026, 12:35 (Europe/London)
Ravi: Monday, 28 December, 2026, 6:05 pm (Asia/Kolkata)
Meena: திங்கள், 28 டிசம்பர், 2026, பிற்பகல் 6:05 (Asia/Kolkata)
Lukas: Montag, 28. Dezember 2026, 13:35 (Europe/Berlin)
Grace: Monday, December 28, 2026, 7:35 AM (America/New_York)
Log:  2026-12-28T12:35:00Z
File: report_20261228_123500.pdf

The exam is stored once, as an Instant. For each student, atZone converts it to local time, and localized formatters render it in the student's language: British English uses a 24-hour clock, Indian English a 12-hour clock with lowercase "pm," Tamil shows the day, month, and "afternoon" (பிற்பகல்) in Tamil script, German puts the day before the month with a period, and US English puts the month first. Two students in Chennai see the same local time in different languages, because zone and locale are independent settings. For machines, the log line uses ISO_INSTANT and the file name uses a compact UTC pattern, which also sorts chronologically as text.

Localized output comes from the JDK's locale data and can change slightly between Java versions (for example, the space before "pm" in Indian English is a narrow non-breaking space). Do not parse or compare localized text in code; use it only for display.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about Date Formatting and get answers drawn from it.

Signed-in readers only.