带有 Joda-Time DateTimeFormatter 的属格(波兰语区域设置)中的月份名称

2022-09-02 04:34:49

我有哪个包含日期2012-12-28,我想用本地化的月份名称(即波兰语中的12月)打印它,这在波兰语中与主格不同(分别为grudniagrudzień)。因为我也想使用我自己创建的自定义格式(AFAIK在Joda-Time中是正确的方法):LocalDateDateTimeFormatterDateTimeFormatterBuilder

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.”。

我的问题是关于用箭头标记的行:我应该如何实现?目前它扩展并有相当多的代码:MonthNameGenitiveDateTimeFieldType

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
    }
  }

}

对我来说似乎很草率,不是防弹的,因为我必须实现许多神奇的方法,而且我只使用并覆盖了一种方法(),而还有其他具有相同名称的方法:DelegatedDateTimeFieldgetAsText(ReadablePartial, Locale)

  • getAsText(long, Locale)
  • getAsText(long)
  • getAsText(ReadablePartial, int, Locale)
  • getAsText(int, Locale)

有没有更好的方法来获得所需的输出(使用)或我的方法正确但非常冗长?DateTimeFormatter

编辑:

我试图用新的JDK8 Time API(类似于Joda,基于JSR-310)实现同样的事情,它可以很容易地完成:

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"));

其中带有自定义月份名称,因此非常易于使用。请参阅 DateTimeFormatterBuilder#appendText(TemporalField, Map)。MONTH_NAMES_GENITIVEMap<Long, String>

有趣的是,在JDK8中,这整个波兰月份名称的玩法是不必要的,因为DateFormatSymbols.getInstance(new Locale(“pl”, “PL”)).getMonths()默认以genitive格式返回月份名称...虽然这个变化对于我的用例是正确的(在波兰语中,我们使用属名中的月份名称说“今天是2012年12月28日”),但在其他一些情况下可能会很棘手(我们使用主格说“这是2012年12月”),并且它是向后不兼容的。


答案 1

你有我的同情 - Joda Time中的现场系统有些复杂。

但是,我建议在这种情况下,最简单的方法实际上是使用 。请注意,此方法仅在您只对打印感兴趣时才有效 - 如果您还需要解析,则生活会变得更加复杂。DateTimeFormatterBuilder.append(DateTimePrinter printer)

在这一点上,你只需要实现,这是相对简单的,特别是如果你很乐意忽略,因为你只对单一文化感兴趣。您可以将所有逻辑(并不是说会有很多逻辑)放在一个方法中,并使其余方法仅委托给该方法。对于采用 a 和 a 的重载,只需构造 a 并调用 ,此时您可以委托给其他方法。DateTimePrinterLocalelongDateTimeZoneDateTimetoLocalDateTime

编辑:事实上,一种选择是编写一个抽象基类,如果你知道你只关心局部值:

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);
    }
}

当然,你可以在一个类中完成所有这些操作,但我可能会将其分解,以使基类将来更易于重用。


答案 2

你真的需要使用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()));

推荐