如何根据区域设置格式化YearMonth和MonthDay?

时间:2016-06-30 12:30:14

标签: java java-8 java-time

使用特定LocalDate在Java 8中格式化Locale可以这样实现:

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);

假设value = LocalDate.now()这会导致:

// 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

正如您所见,Java决定使用哪个分隔符(" - ","。"," /",""等等)使用以及如何对日期元素进行排序(例如,前一个月或反之亦然等 - 在某些地区,可能是那一年首先出现,等等。)

我的问题是:如何根据上面示例中的java.time.YearMonth来格式化java.time.MonthDayLocale

基于这个例子,我希望得到这样的结果......

...代表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

...代表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

当然可能有其他Locale使用完全不同的分隔符和排序。

谢谢!

3 个答案:

答案 0 :(得分:4)

似乎无法在Java-9中修复此Java-8-bug,因为即使feature-extension-complete-date已经结束了。让我们看看它是否会在Java-10中修复,这仍然需要很长时间......

当然,正如这里的一个答案所示,您可以尝试处理给定的本地化日期模式,以便删除不相关的部分。但我仍然认为这种方法是错误的,因为周围仍有很多语言环境。事实上,接受的答案对中国人来说是有缺陷的。本地化的文字是这里的主要问题。也许接受的答案至少可以修复这个重要的语言,但您也可以考虑其他两个具有良好国际化功能的库,这些库可以更可靠的方式解决您的问题

a)ICU4J

DateFormat df = DateFormat.getInstanceForSkeleton(DateFormat.YEAR_MONTH, Locale.CHINESE);
String output = df.format(new Date());
System.out.println("ICU4J=" + output); // 2017年1月

然而,一个问题是缺乏与Java-8类型的互操作性,尤其是MonthDayYearMonth。解决方案需要Date.from(YearMonth.now().atDay(1).atStartOfDay(ZoneId.systemDefault()).toInstant());可能,但很麻烦。

b)我的图书馆Time4J(与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月
带有Java-8的

Interoperability也存在相反的方向。 MonthDay的Time4J对应的是AnnualDate类。

旁注:@Julian收到的中文回答:2017年1(需要修复)

答案 1 :(得分:3)

您需要使用DateTimeFormatter#ofPattern

适用于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));

适用于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));

答案 2 :(得分:3)

如果您使用有限的Locale列表,gevorg提供的解决方案可能是最简单的解决方案。

如果你想让它适用于任何Locale我会建议获得一个语言环境模式然后删除你不感兴趣的部分,一旦你有这个模式你应该删除你不感兴趣的部分并使用生成模式以创建自己的DateTimeFormatter。

这是上面针对MonthDay解释的想法的完整示例。要将其用于YearMonth,请将keep.add('d')替换为keep.add('y')。 (当然MonthDayYearMonth

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();
}

输出结果为:

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

而对于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