使用DateTimeFormatter.ofLocalizedDate

时间:2017-08-14 22:49:31

标签: java date date-formatting java-time

我试图以一种我不必指定顺序的方式格式化MonthDay对象。我正在尝试使用本地化的DateTimeFormatter

我有这段代码:

LocalDate datetime = LocalDate.parse("2017-08-11", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
MonthDay monthday = MonthDay.from(datetime);
System.out.println(monthday.format(DateTimeFormatter.ofPattern("MMMM dd").withLocale(Locale.ENGLISH)));
System.out.println(monthday.format(DateTimeFormatter.ofPattern("MMMM dd").withLocale(Locale.GERMANY)));
System.out.println(monthday.format(DateTimeFormatter.ofPattern("MMMM dd").withLocale(Locale.forLanguageTag("UK"))));

System.out.println(datetime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.ENGLISH)));
System.out.println(datetime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.forLanguageTag("UK"))));
// next line throws exception for java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: YearOfEra
System.out.println(monthday.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.forLanguageTag("UK"))));

前3张照片将按预期打印翻译的月份和日期,但它始终是月份和日期。它不会改变顺序,因为我明确告诉它顺序。

接下来的两个(异常之前)将分别打印:

Aug 11, 2017
11 серп. 2017

注意日期是在月份之前还是之后,具体取决于传递给函数的语言环境。如何使用MonthDay对象执行此操作,因为最后一行以这种方式抛出异常。

3 个答案:

答案 0 :(得分:2)

到目前为止给出的其他答案描述了标准DateTimeFormatter的局限性,另请参阅未解决的相关JDK-issue。通过删除“y”等来编辑本地化日期模式的建议解决方法很棘手,并且可能不适用于所有语言环境,因为模式中存在其他本地化文字。

但是,您也可以考虑使用更强调国际化问题的外部库,并且能够仅使用区域设置信息来格式化月 - 日对象。因此,语言环境决定了字段组件的顺序以及点,空格或其他特殊文字(如中文)。

以下两个选项包含与系统时区相关的必要类型转换:

ICU4J

MonthDay md = MonthDay.now();

GregorianCalendar gcal =
    new GregorianCalendar(
        2000, // avoids possible leap year problems
        md.getMonthValue() - 1,
        md.getDayOfMonth()
    );

DateFormat df =
    DateFormat.getInstanceForSkeleton(
        DateFormat.ABBR_MONTH_DAY,
        Locale.forLanguageTag("en")
    );
System.out.println(df.format(gcal.getTime())); // Aug 15

DateFormat df2 =
    DateFormat.getInstanceForSkeleton(
        DateFormat.ABBR_MONTH_DAY,
        Locale.forLanguageTag("de")
    );
System.out.println(df2.format(gcal.getTime())); // 15. Aug.

DateFormat df3 =
    DateFormat.getInstanceForSkeleton(DateFormat.MONTH_DAY, Locale.forLanguageTag("zh"));
System.out.println(df3.format(gcal.getTime())); // 8月15日

Time4J

MonthDay md = MonthDay.now();

ChronoFormatter<AnnualDate> cf1 =
    ChronoFormatter.ofStyle(DisplayMode.SHORT, Locale.GERMAN, AnnualDate.chronology());
System.out.println(cf1.format(AnnualDate.from(md))); // 15.8.

ChronoFormatter<AnnualDate> cf2 =
    ChronoFormatter.ofStyle(DisplayMode.MEDIUM, Locale.GERMAN, AnnualDate.chronology());
System.out.println(cf2.format(AnnualDate.from(md))); // 15.08.

ChronoFormatter<AnnualDate> cf3 =
    ChronoFormatter.ofStyle(DisplayMode.LONG, Locale.ENGLISH, AnnualDate.chronology());
System.out.println(cf3.format(AnnualDate.from(md))); // Aug 15

ChronoFormatter<AnnualDate> cf4 =
    ChronoFormatter.ofStyle(DisplayMode.FULL, Locale.GERMAN, AnnualDate.chronology());
System.out.println(cf4.format(AnnualDate.from(md))); // 15. August

ChronoFormatter<AnnualDate> cf5 =
    ChronoFormatter.ofStyle(DisplayMode.FULL, Locale.CHINESE, AnnualDate.chronology());
System.out.println(cf5.format(AnnualDate.from(md))); // 8月15日

免责声明:Time4J由我自己编写,以填补空白或改进JSR-310(java.time-package)的其他功能。

答案 1 :(得分:0)

MonthDay未存储年份信息且FormatStyle.MEDIUM需要年份值,这就是格式化工具未找到字段YearOfEra的原因,如果您使用YearMonth使用相同的FormatStyle,但现在用于缺少的字段DayOfMonth

MonthDay API

  

此课程不存储或代表年份,时间或时区。例如,值“12月3日”可以存储在MonthDay中。

您可以将monthday转换为即时消息,或者只使用datetime

答案 2 :(得分:-1)

ofLocalizedDate会返回日期的格式化程序,因此会格式化字段,因此该对象格式化需要拥有所有三个字段。 MonthDay没有字段,这就是它抛出UnsupportedTemporalTypeException的原因。

如果您要打印整个日期(),您必须将年份添加到{{ 1}}对象。您可以使用MonthDay方法:

atYear

这将输出:

  

11серп。 2017

如果您想要当前年份,只需使用System.out.println(monthday.atYear(2017).format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.forLanguageTag("UK")))); 代替Year.now().getValue()

如果您想要2017的同一年,也可以使用datetime.getYear(),或使用LocalDate代替datetime

如果您只想打印,则必须做一些解决方法。

由于本地格式化程序是在JDK中内置的(并且似乎无法更改它们),因此您没有直接的方法来执行此操作,尽管有一些替代方法。

一个(丑陋的)解决方案是设置一年,然后将其从格式化的monthday中删除:

String

输出结果为:

  

11серп。

枯燥的部分是删除每个语言环境中可能存在的所有额外字符。对于英语区域设置,我还必须删除DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.forLanguageTag("UK")); // set year to 2017, then remove "2017" from the formatted string System.out.println(monthday.atYear(2017).format(formatter).replace("2017", "").trim());

,

输出结果为:

  

8月11日

您必须检查所有区域设置的所有额外字符(例如DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.ENGLISH); // remove the year (2017) and the "," from the output System.out.println(monthday.atYear(2017).format(formatter).replace("2017", "").replace(",", "").trim()); ),并从输出中删除它们。或者只使用具有固定非语言环境特定模式的,(正如您在前3次测试中所做的那样)。

作为@BasilBourque's noticed in the comments,我假设年份处于模式的开始或结束。如果年份在中间,则最终结果中会有一些额外的空格,可以使用ofPattern删除(2个或更多空格仅替换为一个)。

另一种选择(由@JodaStephen comment建议)是使用.replaceAll("\\s{2,}", " ")来获取本地化的日期模式。

然后我从模式中删除年份,替换DateTimeFormatterBuilderypatterns used for the year),还删除其他一些字符(例如u和额外空格)

使用生成的模式(没有年份),我使用指定的区域设置创建,并格式化DateTimeFormatter

MonthDay

输出将是:

  

11серп。

只是提醒您此示例可能不完整,因为有些区域设置使用// get date pattern for the specified locale String pattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.forLanguageTag("UK")); pattern = pattern // remove the year (1 or more occurrences of "y" or "u") .replaceAll("[yu]+", "") // replace "," (you can change this to remove any other characters you want) .replaceAll(",", "") // replace 2 or more spaces with just one space and trim to remove spaces in the start or end .replaceAll("\\s{2,}", " ").trim(); // create formatter for the pattern and locale DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattern, Locale.forLanguageTag("UK")); System.out.println(monthday.format(fmt)); /和其他字符作为分隔符,您必须将它们从最终结果中删除。检查您正在使用的所有区域设置,并相应地删除字符。

另一个未涵盖的角落情况是,当您-y为文字时(u内):在这种情况下,不应删除它们。无论如何,您必须检查您正在使用的所有语言环境的格式并相应地处理每个案例。