se22as
se22as

Reputation: 2382

Java 8 time/LocalDate: Convert a date from one format to local specific format

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));
}

Output

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

Answers (2)

se22as
se22as

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

Anonymous
Anonymous

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

Related Questions