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

mku*_*urz 6 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);
Run Code Online (Sandbox Code Playgroud)

假设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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,Java决定使用哪个分隔符(" - ",".","/",""等)以及如何对日期元素进行排序(例如,前一个月,反之亦然等) - 在某些语言环境中它可能是那一年第一,等等.

我的问题是:我如何格式化java.time.YearMonthjava.time.MonthDay依赖于Locale上面的例子?

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

......为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
Run Code Online (Sandbox Code Playgroud)

......为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
Run Code Online (Sandbox Code Playgroud)

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

谢谢!

Jul*_*ian 5

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

如果你想让它与任何区域设置一起工作,我建议获取一个区域设置模式,然后删除你不感兴趣的部分,一旦你有了这个模式,你应该删除你不感兴趣的部分,并使用生成的模式创建您自己的 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();
}
Run Code Online (Sandbox Code Playgroud)

输出将是:

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
Run Code Online (Sandbox Code Playgroud)

对于年月:

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
Run Code Online (Sandbox Code Playgroud)

  • 我刚刚添加了一个完整的示例,它应该适用于任何格式和区域设置,请继续尝试。对于糟糕的解释感到抱歉。 (2认同)
  • 你说得对,固定。我遵循的方法是在每个保留字段后面保留分隔符,然后删除尾随分隔符,因为它在我所知道的语言中最有意义,但这可能不适用于每种语言。例如,使用如下模式会很好:“'YYYY'月'MM'日” (2认同)

Men*_*ild 5

这个Java-8-bug似乎无法在 Java-9 中修复,因为即使功能扩展完成日期已经结束。让我们看看它是否会在 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?
Run Code Online (Sandbox Code Playgroud)

然而,一个问题是缺乏与 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?
Run Code Online (Sandbox Code Playgroud)

与 Java-8 的互操作性也存在于相反的方向。和 Time4J 对应的MonthDay是 class AnnualDate


旁注:@Julian yields for Chinese: 2017?1 的公认答案(需要修正)