3

Im trying to format a date without a year (just day and month, e.g 12.10)

DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT) still yield year for me (12.10.20).

so I tried DateTimeFormatter.ofPattern("dd. MM") but that obviously hardcodes order and dot, which wont make american users happy. (who expect slashes and month first)

How can I internationalize a pattern? Is there some abstract syntax for separators etc?

Meno Hochschild
  • 42,708
  • 7
  • 104
  • 126
urSus
  • 12,492
  • 12
  • 69
  • 89
  • This doesn’t answer your question exactly as it stands, but can you adapt the answers to your needs? [Java: How to display weekday, month and date and no year in locale sensitive manner](https://stackoverflow.com/questions/50277395/java-how-to-display-weekday-month-and-date-and-no-year-in-locale-sensitive-man) The question and the answers do you JSR-310. – Ole V.V. Mar 28 '20 at 04:55
  • Please edit your question to make your examples consistent: `12.10` versus `1.1.20`. – Basil Bourque Mar 28 '20 at 05:16
  • For such a value as your first sentence, you should be using a [`MonthDay`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/MonthDay.html) object. Unfortunately that class does not work with the `DateTimeFormatter.ofLocalized…` formatters to automatically localize. Perhaps you would care to file a feature request on the *java.time* project. This lack has bothered me, but I've never bothered to file a ticket. – Basil Bourque Mar 28 '20 at 05:19
  • @BasilBourque ok – urSus Mar 28 '20 at 21:35
  • It’s not trivial, and I’m not sure we can get every corner case correct. If the built-in format says 12.10.20, would you still want to keep the dot after 10 or not? Probably the best answer differs by locale. The built-in format for Bulgarian is `d.MM.yy 'г'.`, what do we want there? The answer certainly isn’t built-in. – Ole V.V. Mar 28 '20 at 21:47

2 Answers2

1

I don’t think that a solution can be made that gives 100 % satisfactory results for all locales. Let’s give it a shot anyway.

    Locale formattingLocale = Locale.getDefault(Locale.Category.FORMAT);
    String formatPattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
            FormatStyle.SHORT, null, IsoChronology.INSTANCE, formattingLocale);

    // If year comes first, remove it and all punctuation and space before and after it
    formatPattern = formatPattern.replaceFirst("^\\W*[yu]+\\W*", "")
            // If year comes last and is preceded by a space somewhere, break at the space
            // (preserve any punctuation before the space)
            .replaceFirst("\\s\\W*[yu]+\\W*$", "")
            // Otherwise if year comes last, remove it and all punctuation and space before and after it
            .replaceFirst("\\W*[yu]+\\W*$", "");
    DateTimeFormatter monthDayFormatter
            = DateTimeFormatter.ofPattern(formatPattern, formattingLocale);

For comparison I am printing a date both using the normal formatter with year from your question and using my prepared formatter.

    LocalDate exampleDate = LocalDate.of(2020, Month.DECEMBER, 31);
    System.out.format(formattingLocale, "%-11s %s%n",
            exampleDate.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)),
            exampleDate.format(monthDayFormatter));     

Output in French locale (Locale.FRENCH):

31/12/2020  31/12

In Locale.GERMAN:

31.12.20    31.12

Edit: My German girl friend informs me that this is wrong. We should always write a dot after each of the two numbers because both are ordinal numbers. Meno Hochschild, the German author of the other answer, also produces 31.12. with two dots for German.

In Locale.US:

12/31/20    12/31

It might make American users happy. In Swedish (Locale.forLanguageTag("sv")):

2020-12-31  12-31

In a comment I mentioned Bulgarian (bg):

31.12.20 г. 31.12

As far as I have understood, “г.” (Cyrillic g and a dot) is an abbreviation of a word that means year, so when leaving out the year, we should probably leave this abbreviation out too. I’m in doubt whether we ought to include the dot after 12.

Finally Hungarian (hr):

31. 12. 2020. 31. 12.

How the code works: We are first inquiring DateTimeFormatterBuilder about the short date format pattern for the locale. I assume that this is the pattern that your formatter from the question is also using behind the scenes (haven’t checked). I then use different regular expressions to remove the year from different variants, see the comments in the code. Year may be represented by y or u, so I take both into account (in practice y is used). Now it’s trivial to build a new formatter from the modified pattern. For the Bulgarian: from my point of view there is an error in Java regular expressions, they don’t recognize Cyrillic letters as word characters, which is why г was removed too (the error is in documentation too, it claims that a word character is [a-zA-Z_0-9]). We were lucky, though, in our case it produces the result that I wanted.

If you’re happy with a 90 % solution, this would be my suggestion, and I hope you can modify it to any needs your users in some locale may have.

Link: Documentation of Java regular expressions (regex)

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

Well, as Ole pointed out there is no 100% satisfying solution using java.time only. But my library Time4J has found a solution based on the data of the CLDR repository (ICU4J also gives support) using the type AnnualDate (as replacement for MonthDay):

LocalDate yourLocalDate = ...;
MonthDay md = MonthDay.from(yourLocalDate);
AnnualDate ad = AnnualDate.from(md);

ChronoFormatter<AnnualDate> usStyle =
  ChronoFormatter.ofStyle(DisplayMode.SHORT, Locale.US, AnnualDate.chronology());
ChronoFormatter<AnnualDate> germanStyle =
  ChronoFormatter.ofStyle(DisplayMode.SHORT, Locale.GERMANY, AnnualDate.chronology());
System.out.println("US-format: " + usStyle.format(ad)); // US-format: 12/31
System.out.println("German: " + germanStyle.format(ad)); // German: 31.12.
Meno Hochschild
  • 42,708
  • 7
  • 104
  • 126
  • 1
    @OleV.V. That was a typo. I have corrected it. The dot in German format corresponds to CLDR data but is optional in my opinion (as I am a German). – Meno Hochschild Mar 29 '20 at 10:14
  • Aus Neugier, are the localized formats for month-day/annual date included ready-made in [CLDR](https://en.wikipedia.org/wiki/Common_Locale_Data_Repository)? – Ole V.V. Mar 29 '20 at 13:08
  • 1
    @OleV.V.Yes, ready-made in CLDR (but not included in `java.time`, Oracle is sometimes very slow although there is a very old similar [issue](https://bugs.openjdk.java.net/browse/JDK-8168532) with the same root cause. – Meno Hochschild Mar 29 '20 at 17:47