1

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

m.reiter
  • 1,796
  • 2
  • 11
  • 31
  • 1
    You'll have to do something like [this](https://stackoverflow.com/a/30819121/12323248). – akuzminykh Aug 12 '20 at 11:07
  • 1
    As an alternative, you can extract a `LocalDateTime`'s `Month` and find the desired abbreviation via `month.getDisplayName(TextStyle.SHORT, Locale.GERMAN)`... – deHaar Aug 12 '20 at 11:10
  • 1
    I test your code with java and it was correct. I think this is not java problem. – Omid Aug 12 '20 at 11:26
  • 1
    This is a known issue with 3-char abb. of month name using `MMM` with any of date-time formatting API (legacy or new) in Java. Probably you missed to check https://stackoverflow.com/questions/1635681/parsing-a-date-with-short-month-without-dot – Arvind Kumar Avinash Aug 12 '20 at 13:27

2 Answers2

1

You can have the desired / expected abbreviations like this:

import java.time.LocalDateTime
import java.time.Month
import java.time.format.TextStyle
import java.util.Locale

fun main() {
    for (m in 1..12) {
        val month = Month.of(m)
        println(month.getDisplayName(TextStyle.SHORT, Locale.GERMAN))
    }
}

the output comes as

Jan
Feb
Mär
Apr
Mai
Jun
Jul
Aug
Sep
Okt
Nov
Dez

LocalDateTime has a method getMonth() which returns a Month object, that means you can get the month of a LocalDateTime and build the desired String, maybe with a small fun like the following one

fun getAbbreviatedMonth(localDateTime: LocalDateTime, locale: Locale): String {
    return localDateTime.getMonth()
                        .getDisplayName(TextStyle.SHORT, locale)
}

or even without a Locale as argument and a hard-coded Locale.GERMAN.

Don't really know why the German abbreviations are boiled down to 4 characters leaving month names with 4 or less characters as they are and abbreviate the remaining ones to three letters followed by a dot when you use a DateTimeFormatter with the pattern "MMM" on your system, in the Kotlin Playground, it doesn't! The following code produces two equal lines of output:

import java.time.LocalDateTime
import java.time.Month
import java.time.format.TextStyle
import java.util.Locale
import java.time.format.DateTimeFormatter

fun main() {
    var localDateTime = LocalDateTime.now()
    println(getAbbreviatedMonth(localDateTime, Locale.GERMAN))
    println(localDateTime.format(DateTimeFormatter.ofPattern("MMM", Locale.GERMAN)))
}

fun getAbbreviatedMonth(localDateTime: LocalDateTime, locale: Locale): String {
    return localDateTime.getMonth()
                        .getDisplayName(TextStyle.SHORT, locale)
}

which are (execution on 2020-08-12)

Aug
Aug
deHaar
  • 17,687
  • 10
  • 38
  • 51
  • Oh dear, i hadn't anticipated this might be platform- or even device-specific. I'm using this Kotlin Code in an Android-App. In this Android-App, getAbbreviatedMonth() also produces the same (unexpected) outcome as my version. Shame :( – m.reiter Aug 12 '20 at 12:26
  • 1
    @m.reiter Then try to get around it by using the unexpected output and taking a `substring(0, 3)` of it... Don't have more ideas about it at the moment... – deHaar Aug 12 '20 at 12:32
1

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

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161