One alternative is to use a java.text.DateFormat
:
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, Locale.ENGLISH);
The date and time styles (first and second parameters) can be either SHORT
, MEDIUM
, LONG
or FULL
. And you can use Locale.getDefault()
to get the device's default language, if you want.
I'm not sure which combination of styles give what you want - all of them gave me different outputs and none gave me the one you described.
That's because locale specific formats are embeeded in the JVM, and I'm not sure how it varies among different API levels and devices.
If the solution above works, then it's fine. Otherwise, an alternative is to make specific patterns per locale:
// get device's locale
Locale locale = Locale.getDefault();
String pattern = "";
// check language
if ("en".equals(locale.getLanguage())) {
// english
pattern = "dd MMMM yyyy, h.mm a";
} else if ("de".equals(locale.getLanguage())) {
// german
pattern = "dd.MM.yyyy, HH:mm 'Uhr'";
} else {
pattern = // use some default pattern for other languages?
}
SimpleDateFormat sdf = new SimpleDateFormat(pattern, locale);
String formattedDate = sdf.format(new Date());
One detail is that, in my JVM, the AM/PM symbols for English locale are in uppercase, so you may want to adjust it by doing:
// change AM/PM to am/pm (only for English)
if ("en".equals(locale.getLanguage())) {
formattedDate = formattedDate.toLowerCase();
}
java.time API
In API level 26, you can use the java.time API. For lower levels, there's a nice backport, with the same classes and functionalities.
This is much better to work with. The code might look similar, but the classes themselves solves lots of internal issues of the old API.
You can first try to get JVM's localized patterns and see if they match your output:
Locale locale = Locale.getDefault();
FormatStyle style = FormatStyle.MEDIUM;
String pattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(style, style, IsoChronology.INSTANCE, locale);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattern, locale);
Or do the same as above:
// get device's locale
Locale locale = Locale.getDefault();
String pattern = "";
// check language
if ("en".equals(locale.getLanguage())) {
// english
pattern = "dd MMMM yyyy, h.mm a";
} else if ("de".equals(locale.getLanguage())) {
// german
pattern = "dd.MM.yyyy, HH:mm 'Uhr'";
} else {
pattern = ""; // use some default pattern?
}
DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattern, locale);
String formattedDate = LocalDateTime.now().format(fmt);
In my tests, I've got the same problem of having uppercase AM/PM for English. You can solve this by calling toLowerCase()
as above, but this API also allows you to create a more flexible formatter.
And the formatters are thread-safe (while SimpleDateFormat
isn't), so you could create a static map of formatters based on the language and reuse them as many times you want:
// map of formatters
Map<String, DateTimeFormatter> formatterMap = new HashMap<>();
// English formatter
Map<Long, String> customAmPmSymbols = new HashMap<>();
customAmPmSymbols.put(0L, "am");
customAmPmSymbols.put(1L, "pm");
DateTimeFormatter f = new DateTimeFormatterBuilder()
// date/time
.appendPattern("dd MMMM yyyy, h.mm ")
// custom AM/PM symbols (lowercase)
.appendText(ChronoField.AMPM_OF_DAY, customAmPmSymbols)
// create formatter
.toFormatter(Locale.ENGLISH);
// add to map
formatterMap.put("en", f);
// German formatter
formatterMap.put("de", DateTimeFormatter.ofPattern("dd.MM.yyyy, HH:mm 'Uhr'", Locale.GERMAN));
// get device's locale
Locale locale = Locale.getDefault();
DateTimeFormatter fmt = formatterMap.get(locale.getLanguage());
if (fmt != null) {
String formattedDate = LocalDateTime.now().format(fmt);
}