Internationalization with Time in Java

Naveen AnamaneniUpdated · 5 min read

Showing a time to readers around the world involves two separate questions. The first is format: some regions use a 12-hour clock with AM and PM, and others use a 24-hour clock. The second is time zone: 9:30 in the morning in Chicago is 4:30 in the afternoon in Berlin and 9:00 at night in India, all at the same instant. A Locale answers the first question; a ZoneId answers the second. In this lesson, you will learn how to format times for a locale with DateTimeFormatter, and how to show one moment correctly to readers in different zones.

Locale and Time Zone Are Different Things

LocaleZoneId
DecidesHow the time is written: 5:05 PM or 17:05, and the language of namesWhich time it is on the reader's clock
ExampleLocale.GERMANYZoneId.of("Europe/Berlin")
Comes fromThe reader's language settingThe reader's location setting

A German speaker working in Chicago wants the German format and Chicago's time. Never guess one from the other.

Methods

MethodDescription
DateTimeFormatter.ofLocalizedTime(style)It is used to create a formatter for times.
DateTimeFormatter.ofLocalizedDateTime(dateStyle, timeStyle)It creates a formatter for a date and a time together.
formatter.withLocale(locale)It returns a copy that uses the given locale.
zonedDateTime.withZoneSameInstant(zone)It converts a moment to another time zone.

For times, the styles SHORT (hours and minutes) and MEDIUM (with seconds) work with any time. LONG and FULL also print the time zone, so they need a ZonedDateTime.

Example: Time Formats and Time Zones

Times.java▶ Try it
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;
 
public class Times {
    public static void main(String[] args) {
        LocalTime time = LocalTime.of(17, 5, 30);
        Locale[] locales = {Locale.US, Locale.UK, Locale.GERMANY,
            Locale.of("hi", "IN"), Locale.JAPAN};
 
        for (Locale locale : locales) {
            DateTimeFormatter shortTime = DateTimeFormatter
                .ofLocalizedTime(FormatStyle.SHORT).withLocale(locale);
            DateTimeFormatter mediumTime = DateTimeFormatter
                .ofLocalizedTime(FormatStyle.MEDIUM).withLocale(locale);
            System.out.println(locale.toLanguageTag() + "  "
                + time.format(shortTime) + "  |  " + time.format(mediumTime));
        }
 
        // One moment, shown for readers in three places.
        ZonedDateTime meeting = ZonedDateTime.of(
            LocalDateTime.of(2026, 11, 13, 9, 30),
            ZoneId.of("America/Chicago"));
        String[][] readers = {
            {"en-US", "America/Chicago"},
            {"de-DE", "Europe/Berlin"},
            {"hi-IN", "Asia/Kolkata"},
        };
        for (String[] r : readers) {
            Locale locale = Locale.forLanguageTag(r[0]);
            ZonedDateTime local =
                meeting.withZoneSameInstant(ZoneId.of(r[1]));
            DateTimeFormatter f = DateTimeFormatter
                .ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.SHORT)
                .withLocale(locale);
            System.out.println(r[0] + "  " + local.format(f)
                + "  (" + local.getZone() + ")");
        }
    }
}
Output
en-US  5:05 PM  |  5:05:30 PM
en-GB  17:05  |  17:05:30
de-DE  17:05  |  17:05:30
hi-IN  5:05 pm  |  5:05:30 pm
ja-JP  17:05  |  17:05:30
en-US  November 13, 2026, 9:30 AM  (America/Chicago)
de-DE  13. November 2026, 16:30  (Europe/Berlin)
hi-IN  13 नवंबर 2026, 9:00 pm  (Asia/Kolkata)

Explanation:

  1. The first five lines format 17:05:30 for five locales. The US and India use a 12-hour clock with AM/PM; Britain, Germany and Japan use 24 hours.
  2. The meeting is one fixed moment: 9:30 AM in Chicago on 13 November 2026, stored as a ZonedDateTime.
  3. withZoneSameInstant converts that moment to each reader's zone: 16:30 in Berlin and 9:00 PM in Kolkata, the same instant on three clocks.
  4. ofLocalizedDateTime(LONG, SHORT) then writes it in each reader's language and style.

One detail you can't see: in the US output, the space before "AM" and "PM" is a narrow no-break space, not an ordinary space. Recent JDKs follow the Unicode locale data, which uses it so that the time isn't split across two lines. It looks identical on screen, and it is one reason not to compare formatted text with equals or parse it with a hand-written pattern.

Storing and Showing Moments

StepUse
Store or send a momentInstant, or a UTC time in ISO format such as 2026-11-13T15:30:00Z
Convert for a readerinstant.atZone(readerZone)
DisplayofLocalizedDateTime(...).withLocale(readerLocale)

Common Mistakes

Assuming the time zone from the locale. Countries have several zones, and people travel.

Using LocalDateTime for a moment in time. It has no zone, so it doesn't identify an instant. Use Instant or ZonedDateTime.

Hard-coding "AM" and "PM". Many languages have their own words or don't use them.

Comparing formatted output to a fixed string. Hidden characters and data updates make that fragile.

Practice

  1. Easy: Print the current time in SHORT style for Locale.US, Locale.FRANCE and Locale.KOREA.
  2. Harder: Given a meeting at 3:00 PM in Asia/Kolkata, print it for readers in America/New_York, Europe/London and Australia/Sydney, each in an appropriate locale.

Quick check: Time and locales

4 questions
1What decides whether a time shows as 5:05 PM or 17:05?
2What decides which hour appears on a reader's clock?
3Which method converts a moment to another zone without changing the instant?
4In what form should moments be stored?

Key Takeaways

Was this tutorial helpful?

Lesson 506 of 578