我试图格式化没有年份的日期(只是日期和月份,例如12.10)
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
对我来说仍然是一年(12.10.20)。
所以我尝试DateTimeFormatter.ofPattern("dd. MM")
了,但这显然对order 和 dot 进行了硬编码,这不会让美国用户满意。(谁期望斜线和月份优先)
如何使模式国际化?分隔符等是否有一些抽象语法?
我试图格式化没有年份的日期(只是日期和月份,例如12.10)
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
对我来说仍然是一年(12.10.20)。
所以我尝试DateTimeFormatter.ofPattern("dd. MM")
了,但这显然对order 和 dot 进行了硬编码,这不会让美国用户满意。(谁期望斜线和月份优先)
如何使模式国际化?分隔符等是否有一些抽象语法?
好吧,正如 Ole 指出的那样,java.time
仅使用没有 100% 令人满意的解决方案。但是我的库Time4J找到了一个基于 CLDR 存储库数据的解决方案(ICU4J 也提供支持),使用类型为AnnualDate(作为替代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.
我不认为可以制定一个解决方案,为所有语言环境提供 100% 满意的结果。无论如何,让我们试一试。
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);
为了比较,我使用来自您问题的年份的普通格式化程序和使用我准备好的格式化程序打印日期。
LocalDate exampleDate = LocalDate.of(2020, Month.DECEMBER, 31);
System.out.format(formattingLocale, "%-11s %s%n",
exampleDate.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)),
exampleDate.format(monthDayFormatter));
法语语言环境 ( Locale.FRENCH
) 中的输出:
31/12/2020 31/12
在Locale.GERMAN
:
31.12.20 31.12
编辑:我的德国女朋友告诉我这是错误的。我们应该总是在这两个数字后面写一个点,因为它们都是序数。另一个答案的德国作者 Meno Hochschild 也31.12.
用两个点表示德语。
在Locale.US
:
12/31/20 12/31
它可能会让美国用户感到高兴。在瑞典语 ( Locale.forLanguageTag("sv")
) 中:
2020-12-31 12-31
在评论中我提到了保加利亚语(bg
):
31.12.20 г. 31.12
据我所知,“г”。(西里尔字母 g 和点)是表示年份的单词的缩写,所以在省略年份时,我们可能也应该省略这个缩写。我怀疑我们是否应该在 12 之后包含点。
最后是匈牙利语(hr
):
31. 12. 2020. 31. 12.
代码是如何工作的:我们首先询问DateTimeFormatterBuilder
区域设置的短日期格式模式。我认为这是问题中的格式化程序也在幕后使用的模式(尚未检查)。然后我使用不同的正则表达式从不同的变体中删除年份,请参阅代码中的注释。年份可以用y
或表示u
,所以我将两者都考虑在内(在实践y
中使用)。现在从修改后的模式构建一个新的格式化程序是微不足道的。对于保加利亚人:从我的角度来看,Java 正则表达式存在错误,它们无法将西里尔字母识别为单词字符,这就是为什么 г 也被删除(错误也在文档中,它声称单词字符是[a-zA-Z_0-9]
)。不过,我们很幸运,在我们的案例中,它产生了我想要的结果。
如果您对 90% 的解决方案感到满意,这将是我的建议,我希望您可以根据您的用户在某些语言环境中的任何需求对其进行修改。