2013-06-19 6 views
21

Ich habe LocalDate, die Datum 2012-12-28 enthält und ich möchte es mit lokalisiertem Monatsnamen (dh Dezember in Polnisch) in Genitiv in Polnisch drucken unterscheidet sich von Nominativ (Grudnia und Grudzień jeweils). Weil ich mag auch benutzerdefiniertes Format verwenden, habe ich meine eigenen DateTimeFormatter mit DateTimeFormatterBuilder (was AFAIK, um es in Joda-Time ist der richtige Weg):Monatsname im Genitiv (polnisches Gebietsschema) mit Joda-Time DateTimeFormatter

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 

Die Ausgabe sollte "z dnia 28 grudnia 2012 r sein. ".

Meine Frage ist über Linie mit einem Pfeil markiert: Wie soll ich MonthNameGenitive implementieren? Derzeit erstreckt es DateTimeFieldType und hat ziemlich viel 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 
    } 
    } 

} 

scheint schlampig und nicht kugelsicher zu mir, da ich viele magische Methoden zu implementieren hatte und ich bin mit DelegatedDateTimeField und Überschreiben nur eine Methode (getAsText(ReadablePartial, Locale)), während es gibt andere, mit dem gleichen Namen:

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

Gibt es eine besseren Ansatz gewünschte Ausgabe zu erhalten (mit DateTimeFormatter) oder meinem Ansatz richtig noch sehr ausführlich?

EDIT:

Ich habe versucht, das Gleiche mit neuen JDK8 Zeit-API (die Joda ähnlich ist, basiert auf JSR-310) zu erreichen, und es leicht getan werden könnte:

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

wo MONTH_NAMES_GENITIVE ist Map<Long, String> mit benutzerdefinierten Monatsnamen, so ist es sehr einfach zu bedienen. Siehe DateTimeFormatterBuilder#appendText(TemporalField, Map).

Interessanterweise in JDK8 dieses ganzen polnisch-Monats-name-Genitiv Spiel ist nicht notwendig, da DateFormatSymbols.getInstance(new Locale("pl", "PL")).getMonths() kehren Monatsnamen in Genitiv standardmäßig ... Während diese Änderung für meinen Anwendungsfall korrekt ist (in Polnisch, wir sagen, "Heute ist der 28. Dezember 2012" mit Monatsnamen im Genitiv), es kann in einigen anderen Fällen schwierig sein (wir sagen "es ist Dezember 2012" mit Nominativ) und es ist rückwärts inkompatibel.

+1

Ich bin eigentlich ein wenig überrascht, dass es nicht funktioniert, wann der Genitiv zu verwenden und wann die andere (Nominativ?) Version zu verwenden - wir versuchen sicherlich, das Recht in Noda Time zu bekommen. –

+1

@JonSkeet Es scheint, dass in JDK8 die Genitiv/Nominativ-Version über [TextStyle] (http://download.java.net/jdk8/docs/api/java/time/format/TextStyle.html) Enum-Konstanten unterstützt wird 'FULL' /' FULL_STANDALONE' wobei 'FULL' Standard ist und für Polnisch der Genitiv und' FULL_STANDALONE' Nominativ ist - siehe auch [diese XML-Datei mit lokalisierten Namen] (http://hg.openjdk.java.net/threeten/ threeten/jdk/file/73a95812438c/src/teilen/klassen/sun/util/cldr/resources/21_0_1/common/main/pl.xml). So kann '.appendText (ChronoField.MONTH_OF_YEAR, TextStyle.FULL_STANDALONE)' für den Standard-Monatsnamen verwendet werden. – Xaerxess

Antwort

13

Sie haben mein Mitgefühl - das Feldsystem in Joda Zeit etwas kompliziert ist.

Ich schlage jedoch vor, dass in diesem Fall der einfachste Ansatz eigentlich wäre, DateTimeFormatterBuilder.append(DateTimePrinter printer) zu verwenden.Beachten Sie, dass dieser Ansatz nur funktioniert, wenn Sie nur am Drucken interessiert sind - wenn Sie auch analysieren müssen, wird das Leben komplizierter.

An diesem Punkt müssen Sie nur DateTimePrinter implementieren, die relativ einfach ist, vor allem, wenn Sie glücklich sind die Locale zu ignorieren, wie Sie in einer einzigen Kultur nur interessiert sind. Sie können die gesamte Logik (nicht dass es viel davon gibt) in eine einzige Methode einfügen und den Rest der Methoden dazu bringen, diese Methode zu delegieren. Erstellen Sie für die Überladungen, die eine long und eine DateTimeZone nehmen, einfach eine DateTime und rufen Sie toLocalDateTime, an diesem Punkt können Sie an die anderen Methoden delegieren.

EDIT: In der Tat wäre eine Option sein, um eine abstrakte Basisklasse zu schreiben, wenn Sie wüssten, dass Sie nur über die lokalen Werte kümmern würde:

Dann können Sie leicht eine konkrete Unterklasse schreiben, die ignoriert das Gebietsschema und gibt nur den Monat:

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

natürlich Sie dies alles in einer Klasse tun könnten, aber ich würde es wahrscheinlich bricht die Basisklasse mehr wiederverwendbar in der Zukunft zu machen.

+1

Ich sah 'DateTimePrinter' Schnittstelle, aber es ist als * intern * dokumentiert, hat Notiz * Anwendung Benutzer werden dies selten verwenden Klasse direkt * und hat keine öffentlichen Implementierungen, so dass ich stattdessen benutzerdefinierte 'DateTimeFieldType' verwendet. Dein Versuch ist jedoch sauberer und weniger ausführlich, also werde ich ihn definitiv verwenden. BTW Ich frage mich, warum es keinen 'SimpleDateTimePrinter' im Joda-Time-Kern gibt, da' DateTimePrinter' in der 'DateTimeFormatterBuilder' öffentlichen API ist. – Xaerxess

+2

@Xaerxess: Ich hatte den "internen" Teil nicht entdeckt - aber es scheint ziemlich einfach zu sein, richtig zu implementieren, und wie Sie sagen, erscheint es als ein öffentlicher Typ. Ich kann verstehen, dass es * nur selten * von normalen App-Nutzern direkt verwendet wird - aber das ist natürlich nicht das Gleiche wie es ein * Problem * ist :) –

+1

Sie haben recht, es ist wahrscheinlich diese * seltene Verwendung * ;) 'DateTimeParser' ist eine andere" interne, aber öffentliche "Schnittstelle, die in Jodas' DateTimeFormatterBuilder' erscheint und, nachdem ich den Builder mit dem neuen von JSR-311 verglichen habe (siehe bearbeitete Frage), vermute ich, dass "interne" Klassen dort eingefügt wurden Formatierung API erlaubt einige fortgeschrittene Dinge wie aus dieser Frage. Java 8 Time API scheint dieses Problem zu beheben. – Xaerxess

4

Müssen Sie wirklich Joda benutzen? die Monatsnamen zu ersetzen ist trivial die Datumsformatierer in dem Standard-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())); 
+1

Ich würde lieber Joda (Unternehmensempfehlungen) verwenden, und 'SimpleDateFormat' ist nicht Thread-sicher. Da es eine einfache Möglichkeit gibt, Monatsnamen in JDK Date API zu ändern, frage ich mich, ob es in Joda ... – Xaerxess

Verwandte Themen