Java: Format Dates with DateTimeFormatter

Introduction

Java provides an extensive API for handling date and time. In this article, we'll use Java's DateTimeFormatter to format dates - LocalDate, LocalDatetime, LocalTime and ZonedDateTime.

Before formatting dates, you'll have to know How to Get the Current Date and Time in Java.

Format Specifiers

Date and time format specifiers are used for constructing patterns to represent data in a format we'd like to present it in.

In the real world, some date or time components are often represented in more than one way. When it comes to time and date format specifiers in Java, those components have two or more representations too - sometimes it is handy to use a short version, while longer versions are more concise and official.

DateTimeFormatter

Java 8 overhauled the Date/Time API with thread-safe classes that replaced the old and clunky Date and Calendar classes.

This also introduced us to the DateTimeFormatter class, as opposed to the SimpleDateFormat from the old API.

It's worth noting that LocalDate and LocalTime store information about only dates and times respectively, while a LocalDateTime holds information about both the date and time in a single object.

There are two ways of instantiating a DateTimeFormatter:

Writing a pattern:

DateTimeFormatter formatter = DateTimeFormatter
        .ofPattern("EEE, MMM dd. yyyy.");

Using a formatter:

DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;

Using a static method:

1. DateTimeFormatter.ofLocalizedDate(FormatStyle dateStyle)
2. DateTimeFormatter.ofLocalizedTime(FormatStyle timeStyle)
3. DateTimeFormatter.ofLocalizedDateTime(FormatStyle datetimeStyle)
4. DateTimeFormatter.ofLocalizedDateTime(FormatStyle dateStyle, FormatStyle timeStyle)

For example, we could set up a DateTimeFormatter with:

DateTimeFormatter formatter = DateTimeFormatter
        .ofLocalizedTime(FormatStyle.SHORT);

Note that each one of them has a mandatory argument. FormatStyle is a built-in enum that provides a few values - FULL, LONG, MEDIUM and SHORT.

How the available enum patterns look when applied to a ZonedDateTime object is shown in the following table:

FormatStyle Description Date style Time style Date and time style
FULL Highly detailed Thursday, August 13, 2020 12:43:00 AM Central European Summer Time Thursday, August 13, 2020 at 12:43:48 AM Central European Summer Time
LONG Contains most of the details August 13, 2020 12:45:27 AM CEST August 13, 2020 at 12:44:40 AM CEST
MEDIUM Some details included Aug 13, 2020 12:45:49 AM Aug 13, 2020, 12:46:29 AM
SHORT Typically numeric and shortest possible 8/13/20 12:47 AM 8/13/20, 12:47 AM

Formatting LocalTime

In the following code examples, we'll show how to get some of the predefined formatters to do the job for us, as well as making our own. It takes just a few lines of code:

DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM);
LocalTime time = LocalTime.now();

System.out.println(time.format(formatter));

The resulting output is:

5:17:00 AM

Yet, we should be careful when using predefined formatters. In this case, we can't use FormatStyle.LONG or FormatStyle.FULL, because they also yield some time zone information which generally is not stored in LocalTime object.

Let's see how it looks when we make our own pattern:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss a");
LocalTime time = LocalTime.now();
System.out.println(time.format(formatter));

This outputs:

10:58:28 AM

Of course, you're free to fiddle around with the pattern.

Formatting LocalDate

All the classes supported by DateTimeFormatter have a format(DateTimeFormatter f) method, so the process of applying pattern is fairly simple:

DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);
LocalDate d = LocalDate.now();

System.out.println(d.format(formatter));

This piece of code yields:

Thursday, August 13, 2020

Formatting LocalDateTime

Formatting a LocalDateTime is done in the same fashion:

DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.MEDIUM);
LocalDateTime dateTime = LocalDateTime.now();

System.out.println(dateTime.format(formatter));

The resulting output is:

August 13, 2020, 5:29:28 AM

Custom patterns are created in the similar matter:

DateTimeFormatter formatter = DateTimeFormatter
        .ofPattern("eee, MMM dd. yyyy.\nHH:mm:ss a");
LocalDateTime dt = LocalDateTime.now();
System.out.println(dt.format(formatter));

This piece of code yields:

Tue, Aug 25. 2020.
11:05:20 AM

Formatting ZonedDateTime

This time we can casually use the predefined formatter for full output:

DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL);
ZonedDateTime dateTime = ZonedDateTime.now();

System.out.println(dateTime.format(formatter));

This piece of code outputs:

Thursday, August 13, 2020 at 5:32:49 AM Central European Summer Time

When it comes to creating our own patterns for ZonedDateTime, we have all the freedom to use whichever specifier we'd like:

DateTimeFormatter formatter = DateTimeFormatter
        .ofPattern("eee, MMM dd. yyyy.\nHH:mm:ss a - zzzz");
ZonedDateTime dateTime = ZonedDateTime.now();
System.out.println(dateTime.format(formatter));

This outputs:

Tue, Aug 25. 2020.
11:09:27 AM - Central European Summer Time

Formatting rules

Format specifiers for DateTimeFormatter slightly differ from the ones for SimpleDateFormat. If you're used to working with SimpleDateFormat, you'll have to adjust some habits by a tiny bit:

Character Date or time component Content type Example
Y/y Year Year 1969;69
M Month in year Number/Text August;Aug;08
w Week in year Number 21
W Week in month Number 2
d Day in month Number 07;15
D Day in year Number 176
Q/q Quarter of the year Number/Text 2:Q2:2nd quarter
F Day of week in month
(e.g. second Tuesday this month)
Number 2;3
E Day name in week Text Monday;Mon;M
e/c Localized day in week Number/Text Monday;Mon;M;1;01
u Day number of week
(Monday = 1)
Number 1
a Am/pm marker Text PM;AM
h Hour in am/pm (1-12) Number 12
H Hour in day (0-23) Number 0
k Hour in day (1-24) Number 24
K Hour in am/pm (0-11) Number 0
m Minute in hour Number 43
s Second in minute Number 58
S Fraction of second Fraction 965
A Mili of the day Number 1234
n Nano of second Number 566787434
N Nano of day Number 56678743400
z Time zone name Zone name Pacific Standard Time; PST
V Time zone ID Zone ID America/Los_Angeles; Z; -08:30
v Time zone name Zone name Pacific Time; PT0
G Era designator Text AD;Anno Domini;A
  • Text - If less than 4 pattern characters are used, component is presented in the short form. Exactly 4 pattern characters will use full form, and exactly 5 will use the narrow form.
  • Number - The number of pattern characters is the minimum number of digits. If that number is bigger than the number of digits we actually need to represent a number, then the number is prefixed with corresponding amount of zeros. c and F can only have one occurence, while d, H, h, K, k, m and s can have two occurences. Only D can be used up to three times.
  • Number/Text - If 3 or more pattern characters are used, the component is presented in textual form; otherwise in number.
  • Fraction - Possible character count is between 1 and 9. If less than 9 of them is stated, value is truncated with only the most significant digits being output.
  • Year - If the number of pattern characters is 2, the year is truncated to 2 rightmost digits, otherwise it is interpreted as a number.
  • Zone ID - If 2 pattern characters are used, zone ID is output; otherwise the exception is thrown.
  • Zone name - If the pattern character is z the output is the daylight savings aware zone name. If there is insufficient information to determine whether DST applies, the name ignoring Daylight Savings time will be used. If the count of characters is 1, 2 or 3, then the short name is output; if 4 the full name is output. Five or more characters throw an exception. As for the character v, DST is ignored. If 1 pattern character is used, then the short name is output; if 4 are used, the output is full name. Any other number of characters throw an exception.

Conclusion

In this article, we've showcased how to format a LocalDate, LocalTime, LocalDateTime and ZonedDateTime using Java's DateTimeFormatter class.

Author image
Belgrade