Format follows the locale¶
Level: 201 · for anyone whose users are not all in one country
One line: String.format("%.2f", 3.5) produces 3.50 in the US and 3,50 in Poland, Germany and most of Europe — so a number formatted without a locale and then parsed, logged, or written to a file is a bug that only appears on someone else's machine.
Measured¶
default locale for this run : en_US
Locale.ROOT 1,234.50
en-US 1,234.50
pl-PL 1 234,50
de-DE 1.234,50
Now feed the Polish rendering back to a parser that expects a dot:
Double.parseDouble("1234,50") throws NumberFormatException
Rule: Locale.ROOT for machines, a real locale for people.
Never the no-argument overload for either.
The last block is the whole problem in two lines: a number rendered by the default String.format and then handed to Double.parseDouble, which is always locale-neutral. Format is locale-sensitive; parse is not. They are not inverses of each other, and the asymmetry is invisible until the program runs somewhere with a comma.
Where it shows up¶
- A
%,.2famount written into a CSV that a downstream job parses. - A number interpolated into a JSON body or a URL query. JSON's grammar requires a dot; a Polish JVM writes a comma and produces invalid JSON.
- A
toString()on adoubleinside a log line that a metrics pipeline scrapes. - A file name or a cache key built from a formatted number.
The same applies to dates. SimpleDateFormat and DateTimeFormatter both take a locale, and a pattern like MMM is Jan in English, sty in Polish, and janv. in French.
The rule¶
Locale.ROOT for machines, an explicit locale for people, never the default.
For anything that will be read back by a program, prefer a formatter that has no locale at all:
String s = Double.toString(amount); // always a dot
String s = new BigDecimal("1234.50").toPlainString();
BigDecimal is the right type for money anyway, for reasons that have nothing to do with locale — but it also sidesteps this entirely.
See also¶
- Case is locale-sensitive
- The default charset — the other thing the environment used to decide for you