I have LocalDate
which contains date 2012-12-28 and I want to print it with localized month name (i.e. December in Polish) in genitive which in Polish is distinct from nominative (grudnia and grudzień respectively). Because I also want to use custom format I created my own DateTimeFormatter
using DateTimeFormatterBuilder
(which AFAIK is the right way to it in Joda-Time):
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
The output should be "z dnia 28 grudnia 2012 r.".
My question is about line marked with an arrow: how should I implement MonthNameGenitive
? Currently it extends DateTimeFieldType
and has quite much code:
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
}
}
}
Seems sloppy and not bullet-proof to me, since I had to implement many magic methods plus I'm using DelegatedDateTimeField
and overriding only one method (getAsText(ReadablePartial, Locale)
) while there are others with the same name:
getAsText(long, Locale)
getAsText(long)
getAsText(ReadablePartial, int, Locale)
getAsText(int, Locale)
Is there better approach to get desired output (using DateTimeFormatter
) or my approach correct yet very verbose?
EDIT:
I've tried to achieve the same thing with new JDK8 Time API (which is similar to Joda, based on JSR-310) and it could be done easily:
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"));
where MONTH_NAMES_GENITIVE
is Map<Long, String>
with custom month names, so it's very easy to use. See DateTimeFormatterBuilder#appendText(TemporalField, Map)
.
Interestingly, in JDK8 this whole Polish-month-name-genitive play is not necessary because DateFormatSymbols.getInstance(new Locale("pl", "PL")).getMonths()
returns month names in genitive by default... While this change is correct for my use case (in Polish, we say "today is the 28th of December 2012" using month names in genitive), it can be tricky in some other cases (we say "it's December 2012" using nominative) and it's backwards incompatible.
TextStyle
enum constantsFULL
/FULL_STANDALONE
whereFULL
is default and for Polish it's genitive andFULL_STANDALONE
is nominative - see also this XML file with localized names. So.appendText(ChronoField.MONTH_OF_YEAR, TextStyle.FULL_STANDALONE)
can be used for standard month name. – Chlorinate