Reputation: 2382
I get 2 pieces of information from a server call, a date string, and a user locale
String originalDateStr = serverCallToGetADate().getDate();
String localeString = serverCallToGetADate().getLocaleString();
The date is guaranteed to be in the following format regardless of locale
yyyy-MM-dd
The locale is the IETF language code, such as
en-US fr-FR es-ES de-DE etc
I can create a LocalDate object for the date using
DateTimeFormatter originalFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate originalDate = LocalDate.parse(originalDateStr, originalFormatter);
I now want to print out the date with just the year as 4 numbers, month as 2 numbers and day as 2 numbers using a "-" in between the values and using the locale string i also received from the server. For example if the date was 31st July 2021 I want to render the date as
I can not work out how to do this. This is what I have tried
@Test
public void testFormatting() {
String SERVER_FORMAT = "yyyy-MM-dd";
String SERVER_TEST_DATE = "2021-07-31";
DateTimeFormatter serverFormatter = DateTimeFormatter.ofPattern(SERVER_FORMAT);
// can only create a LocalDate from the server value as there is no time part in the string
LocalDate testDate = LocalDate.parse(SERVER_TEST_DATE, serverFormatter);
System.out.println("Original Server Date Value = " + testDate.format(serverFormatter));
System.out.println("\nUsing : testDate.format(serverFormatter.withLocale(locale) - Doesn't work");
Locale localeUS = Locale.forLanguageTag("en-US");
Locale localeUK = Locale.forLanguageTag("en-GB");
System.out.println("Attempting US (should be 07-31-2021) = " + testDate.format(serverFormatter.withLocale(localeUS)));
System.out.println("Attempting UK (should be 31-07-2021) = " + testDate.format(serverFormatter.withLocale(localeUK)));
// in order to use the following coed these we need a time part for the date, so put to start of day
System.out.println("\nUsing DateTimeFormatterBuilder - wrong as it includes / or , and has time displayed");
LocalDateTime testDateTime = testDate.atStartOfDay();
String datePattern_short = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
FormatStyle.SHORT, FormatStyle.MEDIUM, Chronology.ofLocale(localeUS), localeUS);
DateTimeFormatter dateFormatterShort = DateTimeFormatter.ofPattern(datePattern_short);
System.out.println("datePattern SHORT US (should be 07-31-2021) = " + testDateTime.format(dateFormatterShort));
String datePattern_medium = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
FormatStyle.MEDIUM, FormatStyle.MEDIUM, Chronology.ofLocale(localeUS), localeUS);
DateTimeFormatter dateFormatterMedium = DateTimeFormatter.ofPattern(datePattern_medium);
System.out.println("datePattern MEDIUM US (should be 07-31-2021) = " + testDateTime.format(dateFormatterMedium));
String datePattern_long = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
FormatStyle.LONG, FormatStyle.MEDIUM, Chronology.ofLocale(localeUS), localeUS);
DateTimeFormatter dateFormatterLong = DateTimeFormatter.ofPattern(datePattern_long);
System.out.println("datePattern LONG US (should be 07-31-2021) = " + testDateTime.format(dateFormatterLong));
}
Original Server Date Value = 2021-07-31
Using : testDate.format(serverFormatter.withLocale(locale) - Doesn't work
Attempting US (should be 07-31-2021) = 2021-07-31
Attempting UK (should be 31-07-2021) = 2021-07-31
Using DateTimeFormatterBuilder - wrong as it includes / or , and has time displayed
datePattern SHORT US (should be 07-31-2021) = 7/31/21, 12:00:00 am
datePattern MEDIUM US (should be 07-31-2021) = Jul 31, 2021, 12:00:00 am
datePattern LONG US (should be 07-31-2021) = July 31, 2021, 12:00:00 am
Upvotes: 0
Views: 623
Reputation: 2382
Sweeper gave the answer in their comments.
What I was trying to do was not valid. I should always use the standard format for the locale's timezone.
Upvotes: 1
Reputation: 86130
I am unsure why you think you want this and tend to go with Sweeper’s suggestion: You should rather trust that the localized format patterns that Java gets from CLDR (Unicode Common Locale Data Repository) and/or other sources. I don’t imagine that users around the world will be happy about you enforcing a format with hyphens everywhere.
However if you insist, the format pattern string that you get from DateTimeFormatterBuilder
can be modified to your requirement through a few simpler regular expressions.
String[] tags = { "en-US", "fr-FR", "es-ES", "de-DE", "jp-JP" };
LocalDate sampleDate = LocalDate.of(2021, Month.JULY, 31);
for (String langTag : tags) {
Locale loc = Locale.forLanguageTag(langTag);
String pattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
FormatStyle.SHORT, null, IsoChronology.INSTANCE, loc);
// Ensure full year and 2-digit month and day
pattern = pattern.replaceAll("y+", "y")
.replaceAll("M+", "MM")
.replaceAll("d+", "dd");
// Change punctuation to hyphens
pattern = pattern.replaceFirst("^\\W+", "")
.replaceFirst("\\W+$", "")
.replaceAll("\\W+", "-");
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(pattern);
System.out.format("%-5s -> %s%n", langTag, sampleDate.format(dateFormatter));
}
Output from this snippet is:
en-US -> 07-31-2021 fr-FR -> 31-07-2021 es-ES -> 31-07-2021 de-DE -> 31-07-2021 jp-JP -> 2021-07-31
I believe that my code will fail to insert a hyphen if two or more fields are adjacent in the pattern, for example yyMM
. I don’t know whether this occurs for any locale. Also it will not remove any literal letters that are part of the format for some locales. So take it as a starting point and refine to your needs. You may want to test with all available locales in your JVM.
Upvotes: 0