我有LocalDate
,其中包含日期2012-12-28,我想用本地化的月份名称(即12月用波兰语)打印 in genitive ,其中波兰语与nominaltive不同(分别为 grudnia 和grudzień)。因为我也想使用自定义格式,所以我使用DateTimeFormatter
创建了自己的DateTimeFormatterBuilder
(在Joda-Time中,AFAIK是正确的方式):
private static final DateTimeFormatter CUSTOM_DATE_FORMATTER
= new DateTimeFormatterBuilder()
.appendLiteral("z dnia ")
.appendDayOfMonth(1)
.appendLiteral(' ')
.appendText(new MonthNameGenitive()) // <--
.appendLiteral(' ')
.appendYear(4, 4)
.appendLiteral(" r.")
.toFormatter()
.withLocale(new Locale("pl", "PL")); // not used in this case apparently
输出应为“ z dnia 28 grudnia 2012 r。”。
我的问题是关于标有箭头的行:我应该如何实施MonthNameGenitive
?目前它扩展了DateTimeFieldType
并且有很多代码:
final class MonthNameGenitive extends DateTimeFieldType {
private static final long serialVersionUID = 1L;
MonthNameGenitive() {
super("monthNameGenitive");
}
@Override
public DurationFieldType getDurationType() {
return DurationFieldType.months();
}
@Override
public DurationFieldType getRangeDurationType() {
return DurationFieldType.years();
}
@Override
public DateTimeField getField(final Chronology chronology) {
return new MonthNameGenDateTimeField(chronology.monthOfYear());
}
private static final class MonthNameGenDateTimeField
extends DelegatedDateTimeField {
private static final long serialVersionUID = 1L;
private static final ImmutableList<String> MONTH_NAMES =
ImmutableList.of(
"stycznia", "lutego", "marca", "kwietnia", "maja", "czerwca",
"lipca", "sierpnia", "września", "października", "listopada",
"grudnia");
private MonthNameGenDateTimeField(final DateTimeField field) {
super(field);
}
@Override
public String getAsText(final ReadablePartial partial,
final Locale locale) {
return MONTH_NAMES.get(
partial.get(this.getType()) - 1); // months are 1-based
}
}
}
看起来很邋and而不是防弹,因为我必须实现许多魔术方法,而且我正在使用DelegatedDateTimeField
并且只覆盖一种方法(getAsText(ReadablePartial, Locale)
),而其他方法则相同名:
getAsText(long, Locale)
getAsText(long)
getAsText(ReadablePartial, int, Locale)
getAsText(int, Locale)
是否有更好的方法来获得所需的输出(使用DateTimeFormatter
)或我的方法正确但非常详细?
修改
我尝试使用新的JDK8 Time API(类似于基于JSR-310的Joda)实现相同的功能,并且可以轻松完成:
private static final java.time.format.DateTimeFormatter JDK8_DATE_FORMATTER
= new java.time.format.DateTimeFormatterBuilder()
.appendLiteral("z dnia ")
.appendValue(ChronoField.DAY_OF_MONTH, 1, 2, SignStyle.NORMAL)
.appendLiteral(' ')
.appendText(ChronoField.MONTH_OF_YEAR, MONTH_NAMES_GENITIVE) // <--
.appendLiteral(' ')
.appendValue(ChronoField.YEAR, 4)
.appendLiteral(" r.")
.toFormatter()
.withLocale(new Locale("pl", "PL"));
其中MONTH_NAMES_GENITIVE
是Map<Long, String>
,其中包含自定义月份名称,因此它非常易于使用。请参阅DateTimeFormatterBuilder#appendText(TemporalField, Map)
。
有趣的是,在JDK8中,这个整个波兰月名称的游戏都不是必需的,因为DateFormatSymbols.getInstance(new Locale("pl", "PL")).getMonths()
默认返回属性中的月份名称... 虽然这种改变对我来说是正确的用例(在波兰语中,我们说“今天是2012年12月28日”,使用月份名称),在其他一些情况下(我们说“2012年12月”使用名词)可能会很棘手,并且它向后兼容。
答案 0 :(得分:13)
你有我的同情 - Joda Time的现场系统有点复杂。
但是,我建议在这种情况下,最简单的方法实际上是使用DateTimeFormatterBuilder.append(DateTimePrinter printer)
。请注意,如果您只对打印感兴趣,此方法仅可以正常工作 - 如果您需要解析,生活也会变得更复杂。
此时您只需要实现DateTimePrinter
,相对直截了当,特别是如果您乐意忽略Locale
,因为您只对此感兴趣单一的文化。您可以将所有逻辑(而不是它的大部分内容)放在一个方法中,并使其余的方法只委托给该方法。对于采用long
和DateTimeZone
的重载,只需构造一个DateTime
并调用toLocalDateTime
,此时您可以委托其他方法。
编辑:事实上,如果您知道自己只关心本地值,那么可以选择编写一个抽象基类:
public abstract class SimpleDateTimePrinter implements DateTimePrinter {
protected abstract String getText(ReadablePartial partial, Locale locale);
@Override
public void printTo(StringBuffer buf, long instant, Chronology chrono,
int displayOffset, DateTimeZone displayZone, Locale locale) {
DateTime dateTime = new DateTime(instant, chrono.withZone(displayZone));
String text = getText(dateTime.toLocalDateTime(), locale);
buf.append(text);
}
@Override
public void printTo(Writer out, long instant, Chronology chrono,
int displayOffset, DateTimeZone displayZone, Locale locale)
throws IOException {
DateTime dateTime = new DateTime(instant, chrono.withZone(displayZone));
String text = getText(dateTime.toLocalDateTime(), locale);
out.write(text);
}
@Override
public void printTo(StringBuffer buf, ReadablePartial partial, Locale locale) {
buf.append(getText(partial, locale));
}
@Override
public void printTo(Writer out, ReadablePartial partial, Locale locale)
throws IOException {
out.write(getText(partial, locale));
}
}
然后,您可以轻松编写忽略区域设置的具体子类,并返回月份:
public class PolishGenitiveMonthPrinter extends SimpleDateTimePrinter {
private static final ImmutableList<String> MONTH_NAMES =
ImmutableList.of(
"stycznia", "lutego", "marca", "kwietnia", "maja", "czerwca",
"lipca", "sierpnia", "września", "października", "listopada",
"grudnia");
private static final int MAX_MONTH_LENGTH;
static {
int max = 0;
for (String month : MONTH_NAMES) {
if (month.length() > max) {
max = month.length();
}
}
MAX_MONTH_LENGTH = max;
}
@Override
public int estimatePrintedLength() {
return MAX_MONTH_LENGTH;
}
@Override
protected String getText(ReadablePartial partial, Locale locale) {
int month = partial.get(DateTimeFieldType.monthOfYear());
return MONTH_NAMES.get(month - 1);
}
}
当然,可以在一个类中完成所有操作,但我可能会将其分解,以使基类在将来更具可重用性。
答案 1 :(得分:4)
你真的需要使用Joda吗?使用标准Java API中的日期格式化器来替换月份名称是微不足道的:
SimpleDateFormat sdf = new SimpleDateFormat("'z dnia' dd MMMM yyyy 'r.'");
DateFormatSymbols dfs = sdf.getDateFormatSymbols();
dfs.setMonths(
new String[] {
"stycznia", "lutego", "marca", "kwietnia", "maja", "czerwca",
"lipca", "sierpnia", "września", "października", "listopada",
"grudnia"
});
sdf.setDateFormatSymbols(dfs);
System.out.println(
sdf.format(new GregorianCalendar(2012, Calendar.DECEMBER, 28).getTime()));