Internationalization with Time in Java
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
Locale | ZoneId | |
|---|---|---|
| Decides | How the time is written: 5:05 PM or 17:05, and the language of names | Which time it is on the reader's clock |
| Example | Locale.GERMANY | ZoneId.of("Europe/Berlin") |
| Comes from | The reader's language setting | The 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
| Method | Description |
|---|---|
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
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() + ")");
}
}
}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:
- 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.
- The meeting is one fixed moment: 9:30 AM in Chicago on 13 November 2026, stored as a
ZonedDateTime. withZoneSameInstantconverts that moment to each reader's zone: 16:30 in Berlin and 9:00 PM in Kolkata, the same instant on three clocks.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
| Step | Use |
|---|---|
| Store or send a moment | Instant, or a UTC time in ISO format such as 2026-11-13T15:30:00Z |
| Convert for a reader | instant.atZone(readerZone) |
| Display | ofLocalizedDateTime(...).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
- Easy: Print the current time in
SHORTstyle forLocale.US,Locale.FRANCEandLocale.KOREA. - Harder: Given a meeting at 3:00 PM in
Asia/Kolkata, print it for readers inAmerica/New_York,Europe/LondonandAustralia/Sydney, each in an appropriate locale.
Key Takeaways
Was this tutorial helpful?