Search code examples
javadate-formattingjava-time

When I format a LocalDate to dd.MMM.YYYY I get 01.Jan..2000 with two dots


I am trying to format LocalDate variables to dd.MMM.YYYY with:

DateTimeFormatter.ofPattern("dd.MMM.yyyy")

The problem is that more half the time I get two dots. For example 01-01-2000 goes to 01.Jan..2000.

I know why I have this problem, because of the three Ms. When I use dd.MM.yyyy I get to 01.01.2000 without issue. The third M is the problem.

How can I fix this?


Solution

  • The cause of your problem is that the abbreviations for months are locale specific:

    • In some locales there is a dot (period) to indicate abbreviations1; Locale.CANADA for example. In others there isn't; Locale.ENGLISH for example.
    • In the locales where a dot indicates abbreviation, you may or may not find that there is dot when the name of months doesn't need abbreviating. For example the name of the month May is only three letters, so May. indicating that this is an abbreviation would be illogical.

    There are various ways to deal with this, including:

    1. Don't fix it. The output with doubled dots in some cases and not others is logically correct (by a certain logic)2, even though it looks odd.

    2. My preferred way would be to use a different output format. Don't use dot as a separator.

      Using dot characters as separators is ... unconventional ... and when you combine this with abbreviated month names, you get this awkward edge-case.

      Sure there are ways to deal with this, but consider that other people might then run into an equivalent problem if they need to parse your dates in their code-base.

    3. Hard wire your DateTimeFormatter to an existing Locale where there are no dots in the abbreviated names.

      There is a theoretical risk that they may decide to change the abbreviations in a standard Locale. But doubt that they would, because such a change is liable to break customer code which is implicitly locale dependent ... like yours would be.

    4. Create a custom Locale and use that when creating the DateTimeFormatter.

    5. Use DateTimeFormatterBuilder for create the formatter. To deal with the month, use appendText(TemporalField field, Map<Long,String> textLookup) with a lookup table that contains exactly the abbreviations that you want to use.

      Depending on how you "append" the other fields, your formatter can be totally or partially locale independent.


    Of these, 2. and 5. are the most "correct", in my opinion. Ole's answer illustrates these options with code.


    1 - See this article on American English grammar - When you need periods after abbreviations.
    2 - The problem would be convincing people that "looks odd but is logical" is better than "looks nice but is illogical". I don't think you would win this argument ...