How to format YearMonth and MonthDay depending on the locale?

Formatting a LocalDate in Java 8 using a specific Locale can be done as follows:

 DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(myLocale).format(value); DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(myLocale).format(value); DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).withLocale(myLocale).format(value); DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(myLocale).format(value); 

Assuming value = LocalDate.now() , this will result in:

 // myLocale = Locale.ENGLISH 6/30/16 Jun 30, 2016 June 30, 2016 Thursday, June 30, 2016 // myLocale = Locale.GERMAN 30.06.16 30.06.2016 30. Juni 2016 Donnerstag, 30. Juni 2016 // myLocale = new Locale("es", "ES") 30/06/16 30-jun-2016 30 de junio de 2016 jueves 30 de junio de 2016 

As you can see, Java decides which separator ("-", ".", "/", "", Etc.) to use and how to sort date elements (for example, month to day or vice versa, etc. in some places it may be that year in the first place, etc.).

My question is: How can I format java.time.YearMonth and java.time.MonthDay depending on the Locale , as in the example above?

Based on this example, I would expect results like this ...

... for YearMonth :

 // myLocale = Locale.ENGLISH 6/16 Jun, 2016 June, 2016 June, 2016 // myLocale = Locale.GERMAN 06.16 06.2016 Juni 2016 Juni 2016 // myLocale = new Locale("es", "ES") 06/16 jun-2016 de junio de 2016 de junio de 2016 

... for MonthDay :

 // myLocale = Locale.ENGLISH 6/30 Jun 30 June 30 June 30 // myLocale = Locale.GERMAN 30.06. 30.06. 30. Juni 30. Juni // myLocale = new Locale("es", "ES") 30/06 30-jun 30 de junio de 30 de junio de 

Of course, there may be other Locale that use completely different delimiters and orderings.

Thanks!

+5
source share
3 answers

It seems that this Java-8 error cannot be fixed in Java-9 because even feature-extension-full-date has already been completed. Let's see if it will be fixed in Java-10, which is still a long time ...

Of course, as you can give one answer here, you can try to process this localized date template to remove unnecessary parts. But I still think this approach is wrong, because there are still so many locales around. Indeed, the accepted answer is spoiled for the Chinese. The main problem here is localized literals. Perhaps the accepted answer may be fixed, at least for this important language, but you can also consider two other libraries with good internationalization capabilities that can solve your problem in a more reliable way .

a) ICU4J

 DateFormat df = DateFormat.getInstanceForSkeleton(DateFormat.YEAR_MONTH, Locale.CHINESE); String output = df.format(new Date()); System.out.println("ICU4J=" + output); // 2017ๅนด1ๆœˆ 

However, one of the problems is the lack of interoperability with the Java-8 types, especially MonthDay and YearMonth . The solution requires something like Date.from(YearMonth.now().atDay(1).atStartOfDay(ZoneId.systemDefault()).toInstant()); Possible, but cumbersome.

b) my Time4J library (with the same database as ICU4J)

 ChronoFormatter<CalendarMonth> cf = ChronoFormatter.ofStyle(DisplayMode.FULL, Locale.CHINESE, CalendarMonth.chronology()); CalendarMonth cm = CalendarMonth.from(YearMonth.now()); // or: CalendarMonth.nowInSystemTime() System.out.println("Time4J=" + cf.format(cm)); // 2017ๅนด1ๆœˆ 

Interaction with Java-8 exists in the opposite direction. And the Time4J counterpart for MonthDay is the AnnualDate class.


Side note: @Julian's accepted answer gives for Chinese: 2017 ๅนด 1 (need to fix)

+4
source

You need to use DateTimeFormatter#ofPattern

For YearMonth

 YearMonth source = YearMonth.now(); DateTimeFormatter english = DateTimeFormatter.ofPattern("MMMM, yyyy", Locale.ENGLISH); DateTimeFormatter german = DateTimeFormatter.ofPattern("MMMM yyyy", Locale.GERMAN); System.out.println(source.format(english)); System.out.println(source.format(german)); 

For MonthDay

 MonthDay source = MonthDay.now(); DateTimeFormatter english = DateTimeFormatter.ofPattern("MMMM dd", Locale.ENGLISH); DateTimeFormatter german = DateTimeFormatter.ofPattern("dd. MMMM", Locale.GERMAN); System.out.println(source.format(english)); System.out.println(source.format(german)); 
+3
source

The solution provided by gevorg is probably the easiest solution if you use a limited list of Locale.

If you want it to work with any Locale, I would suggest getting the locale template, and then remove the parts that you are not interested in, after you get this template, you should remove the part that you are not interested in and use as a result it turns out a template for creating your own DateTimeFormatter.

This is a complete example of the idea described above for MonthDay . To use it for YearMonth , replace keep.add('d') with keep.add('y') . (and of course MonthDay with YearMonth )

 ArrayList<Locale> locales = new ArrayList<Locale>(); locales.add(Locale.ENGLISH); locales.add(Locale.GERMAN); locales.add(new Locale("es", "ES")); locales.add(Locale.US); ArrayList<FormatStyle> styles = new ArrayList<FormatStyle>(); styles.add(FormatStyle.SHORT); styles.add(FormatStyle.MEDIUM); styles.add(FormatStyle.LONG); styles.add(FormatStyle.FULL); ArrayList<Character> keep = new ArrayList<Character>(); keep.add('d'); keep.add('M'); for (FormatStyle style : styles) { for (Locale myLocale : locales) { String myPattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(style, null, IsoChronology.INSTANCE, myLocale); boolean separator = false; boolean copy = true; String newPattern = ""; for (char c : myPattern.toCharArray()) { if (c == '\'') { separator = !separator; } if (!separator) { if (Character.isAlphabetic(c)) { if (keep.contains(c)) { copy = true; } else { copy = false; } } } if (copy) { newPattern = newPattern + c; } } char lastChar = newPattern.charAt(newPattern.length() - 1); while (!keep.contains(lastChar)) { if (lastChar == '\'') { newPattern = newPattern.substring(0, newPattern.length() - 1); newPattern = newPattern.substring(0, newPattern.lastIndexOf('\'')); } else { newPattern = newPattern.substring(0, newPattern.length() - 1); } lastChar = newPattern.charAt(newPattern.length() - 1); } System.out.println(DateTimeFormatter.ofPattern(newPattern, myLocale).format(YearMonth.now())); } System.out.println(); } 

The conclusion will be:

 6/30 Jun 30 June 30 June 30 30.06 30.06 30. Juni 30. Juni 30/06 30-jun 30 de junio 30 de junio 6/30 Jun 30 June 30 June 30 

And for YearMonth:

 6/16 Jun 2016 June 2016 June 2016 06.16 06.2016 Juni 2016 Juni 2016 06/16 jun-2016 junio de 2016 junio de 2016 6/16 Jun 2016 June 2016 June 2016 
+3
source

All Articles