How to force LocalDateTime Month to be 3 letters long

Tags: ,



My goal is to use LocalDateTime and display a month with exactly 3 letters.

For the English language, this is easy:

val englishFormatter = DateTimeFormatter.ofPattern("MMM", Locale.ENGLISH)

for (month in 1..12) {
    println(LocalDateTime.of(0, month, 1, 0, 0)
                         .format(englishFormatter))
}

The result is as expected:

Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec

For the German language (as above, only with Locale.GERMAN), the result is unexpected:

Jan. Feb. März Apr. Mai Juni Juli Aug. Sep. Okt. Nov. Dez.

While the abbreviations are all common in german, “März”, “Juni” and “Juli” haven’t been shortened (“Mai” doesn’t need to be shortened). Also, most month contain more than 3 letters (notice the dot!)

Is there a way to shorten those, two?
e.g.

März ⇒ Mrz.
Juni ⇒ Jun.
Juli ⇒ Jul.

Btw: the code is in Kotlin, but Kotlin is using Java’s LocalDateTime. Therefore tagged Java

Edit: i’m running this code on Android 7.0

Answer

Controlling exactly what month abbreviations Java gives you is hard, I don’t think you will want to bother. Java gets its locale data from up to four sources, and those sources generally come in versions. So even if you have managed to get your results exactly right, they may be different in the next Java version. I suggest that you choose between two options:

  1. Live with what you get. It’s well worked through and not unreasonable. Will any German have any trouble reading and understanding the abbreviations you mention?
  2. If you have very specific requirements for the abbreviations, such as Mrz rather than Mär, hardcode the abbreviations you require, and then you know they will stay that way no matter if the locale provider and/or the locale data version changes.

As a compromise between the two you may try to select the locale data provider through defining the system property java.locale.providers. As I said, the locale data you get from your provider may change in a future version.

If you want to hardcode your own preferred abbreviations, you can still build a DateTimeFormatter that uses your abbreviations. For a simple demonstration in Java:

    Map<Long, String> monthAbbreviations = Map.ofEntries(
            Map.entry(1L, "Jan"), Map.entry(2L, "Feb"), Map.entry(3L, "Mrz"),
            Map.entry(4L, "Apr"), Map.entry(5L, "Mai"), Map.entry(6L, "Jun"),
            Map.entry(7L, "Jul"), Map.entry(8L, "Aug"), Map.entry(9L, "Sep"),
            Map.entry(10L, "Okt"), Map.entry(11L, "Nov"), Map.entry(12L, "Dez"));
    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendText(ChronoField.MONTH_OF_YEAR, monthAbbreviations)
            .toFormatter(Locale.GERMAN);
    
    String allAbbreviations = Arrays.stream(Month.values())
            .map(formatter::format)
            .collect(Collectors.joining(" "));
    System.out.println(allAbbreviations);

Output is:

Jan Feb Mrz Apr Mai Jun Jul Aug Sep Okt Nov Dez

The locale data providers are, from the documentation of LocaleServiceProvider:

Java Runtime Environment provides the following four locale providers:

  • “CLDR”: A provider based on Unicode Consortium’s CLDR Project.
  • “COMPAT”: represents the locale sensitive services that is compatible with the prior JDK releases up to JDK8 (same as JDK8’s “JRE”).
  • “SPI”: represents the locale sensitive services implementing the subclasses of this LocaleServiceProvider class.
  • “HOST”: A provider that reflects the user’s custom settings in the underlying operating system. This provider may not be available, depending on the Java Runtime Environment implementation.
  • “JRE”: represents a synonym to “COMPAT”. This name is deprecated and will be removed in the future release of JDK.

Link



Source: stackoverflow