如何根据区域设置设置年月月日的格式?
How to format YearMonth and MonthDay depending on a Locale?
使用特定的 Locale
在 Java 8 中格式化 LocalDate
可以像这样实现:
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.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
... 对于 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
使用完全不同的分隔符和顺序。
谢谢!
您需要使用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));
如果您将使用有限的区域设置列表,gevorg 提供的解决方案可能是最简单的解决方案。
如果你想让它与任何语言环境一起工作,我建议你获得一个语言环境模式,然后删除你不感兴趣的部分,一旦你有了这个模式,你应该删除你不感兴趣的部分并且使用生成的模式创建您自己的 DateTimeFormatter。
这是上面为 MonthDay
解释的想法的完整示例。为了将它用于 YearMonth
,将 keep.add('d')
替换为 keep.add('y')
。 (当然 MonthDay
和 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();
}
输出将是:
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
年月:
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
看来这个Java-8-bug cannot be fixed in Java-9 because even the 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 类型的互操作性,尤其是 MonthDay
和 YearMonth
。解决方案需要类似 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月
Interoperability与Java-8也存在反方向。 MonthDay
的 Time4J 对应物是 class AnnualDate
.
旁注:@Julian 接受的中文答案为:2017 年 1(需要修复)
使用特定的 Locale
在 Java 8 中格式化 LocalDate
可以像这样实现:
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.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
... 对于 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
使用完全不同的分隔符和顺序。
谢谢!
您需要使用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));
如果您将使用有限的区域设置列表,gevorg 提供的解决方案可能是最简单的解决方案。
如果你想让它与任何语言环境一起工作,我建议你获得一个语言环境模式,然后删除你不感兴趣的部分,一旦你有了这个模式,你应该删除你不感兴趣的部分并且使用生成的模式创建您自己的 DateTimeFormatter。
这是上面为 MonthDay
解释的想法的完整示例。为了将它用于 YearMonth
,将 keep.add('d')
替换为 keep.add('y')
。 (当然 MonthDay
和 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();
}
输出将是:
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
年月:
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
看来这个Java-8-bug cannot be fixed in Java-9 because even the 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 类型的互操作性,尤其是 MonthDay
和 YearMonth
。解决方案需要类似 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月
Interoperability与Java-8也存在反方向。 MonthDay
的 Time4J 对应物是 class AnnualDate
.
旁注:@Julian 接受的中文答案为:2017 年 1(需要修复)