diff --git a/calendar/src/main/java/net/time4j/calendar/frenchrev/FrenchRepublicanCalendar.java b/calendar/src/main/java/net/time4j/calendar/frenchrev/FrenchRepublicanCalendar.java index 0b631c09d..3e8ab22d3 100644 --- a/calendar/src/main/java/net/time4j/calendar/frenchrev/FrenchRepublicanCalendar.java +++ b/calendar/src/main/java/net/time4j/calendar/frenchrev/FrenchRepublicanCalendar.java @@ -112,6 +112,69 @@ * *

Furthermore, all elements defined in {@code EpochDays} are supported.

* + *

Formatting and parsing: When using format patterns the + * {@link net.time4j.format.expert.PatternType#DYNAMIC dynamic pattern type} + * is strongly recommended instead of CLDR-like pattern types because this calendar + * is structurally different from month-based calendars. Following symbol-element + * table holds:

+ * + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Mapping of dynamic pattern symbols
elementsymboltype
ERAGtext
YEAR_OF_ERAYnumber
MONTH_OF_YEARMtext
SANSCULOTTIDESStext
DAY_OF_MONTHDnumber
DAY_OF_DECADECtext
DAY_OF_WEEKEtext
+ *
+ * + *

Note: The standalone form of some enums like the republican month can be printed in a capitalized way + * if the formatter is first constructed on builder level by using a sectional attribute for the output context. + * Alternatively, users can simply modify the formatter by calling + * {@code f.with(Attributes.OUTPUT_CONTEXT, OutputContext.STANDALONE)}.

+ * + *

Furthermore: The abbreviated form of the republican month is usually numeric with two arabic digits. For + * more control about the numeric representation, the builder offers extra fine-tuned methods.

+ * + *

It is strongly recommended to use the or-operator "|" in format patterns because not every date of + * this calendar has a month. Example:

+ * + *
+ *    ChronoFormatter<FrenchRepublicanCalendar> f =
+ *      ChronoFormatter
+ *        .setUp(FrenchRepublicanCalendar.axis(), Locale.FRENCH)
+ *        .startSection(Attributes.NUMBER_SYSTEM, NumberSystem.ARABIC)
+ *        .addPattern("[D. MMMM|SSSS]', an 'Y", PatternType.DYNAMIC)
+ *        .endSection()
+ *        .build();
+ *
+ *    FrenchRepublicanCalendar cal = PlainDate.of(2018, 9, 23).transform(FrenchRepublicanCalendar.axis());
+ *    System.out.println(f.format(cal)); // output => 1. vendémiaire, an 227
+ *
+ *    cal = cal.minus(CalendarDays.ONE);
+ *    System.out.println(f.format(cal)); // output => jour de la révolution, an 226
+ * 
+ * * @author Meno Hochschild * @see FrenchRepublicanEra * @see FrenchRepublicanMonth @@ -158,6 +221,69 @@ * *

Au&slig;erdem werden alle Elemente von {@code EpochDays} unterstützt.

* + *

Formatieren und Interpretation: Wenn Formatmuster verwendet werden, + * wird der {@link net.time4j.format.expert.PatternType#DYNAMIC dynamische Formatmustertyp} + * anstelle von CLDR-basierten Formatmustertypen empfohlen, weil dieser Kalender strukturell + * von monatsbasierten Kalendern verschieden ist. Folgende Symbol-Element-Tabelle gilt:

+ * + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Zuordnung von dynamischen Mustersymbolen
elementsymboltype
ERAGtext
YEAR_OF_ERAYnumber
MONTH_OF_YEARMtext
SANSCULOTTIDESStext
DAY_OF_MONTHDnumber
DAY_OF_DECADECtext
DAY_OF_WEEKEtext
+ *
+ * + *

Hinweis: Die standalone-Form einiger Enums wie dem republikanischen Monat ist in einer + * kapitalisierten Weise erhältlich, wenn der Formatierer zuerst mittels seines builder + * konstruiert und ein sektionales Attribut für den Ausgabekontext verwendet wird. Alternativ + * kann man am Formatierer {@code f.with(Attributes.OUTPUT_CONTEXT, OutputContext.STANDALONE)} aufrufen.

+ * + *

Außerdem ist die abgekürzte Schreibweise des republikanischen Monats gewöhnlich + * numerisch mit zwei arabischen Ziffern. Um mehr Kontrolle über die numerische Repräsentation + * zu bekommen, bietet der builder weitere feingranulare Methoden an.

+ * + *

Weil nicht jedes Datum dieses Kalenders einen Monat hat, ist es angeraten, mit dem Oder-Operator + * "|" in der Formatierung zu arbeiten. Beispiel:

+ * + *
+ *    ChronoFormatter<FrenchRepublicanCalendar> f =
+ *      ChronoFormatter
+ *        .setUp(FrenchRepublicanCalendar.axis(), Locale.FRENCH)
+ *        .startSection(Attributes.NUMBER_SYSTEM, NumberSystem.ARABIC)
+ *        .addPattern("[D. MMMM|SSSS]', an 'Y", PatternType.DYNAMIC)
+ *        .endSection()
+ *        .build();
+ *
+ *    FrenchRepublicanCalendar cal = PlainDate.of(2018, 9, 23).transform(FrenchRepublicanCalendar.axis());
+ *    System.out.println(f.format(cal)); // Ausgabe => 1. vendémiaire, an 227
+ *
+ *    cal = cal.minus(CalendarDays.ONE);
+ *    System.out.println(f.format(cal)); // Ausgabe => jour de la révolution, an 226
+ * 
+ * * @author Meno Hochschild * @see FrenchRepublicanEra * @see FrenchRepublicanMonth @@ -196,14 +322,14 @@ public final class FrenchRepublicanCalendar /*[deutsch] *

Repräsentiert das republikanische Jahr gezählt seit 1792-09-22 (im Bereich 1-1202).

*/ - @FormattableElement(format = "y") + @FormattableElement(format = "Y") public static final StdCalendarElement YEAR_OF_ERA = new StdIntegerDateElement<>( "YEAR_OF_ERA", FrenchRepublicanCalendar.class, 1, MAX_YEAR, - 'y', + 'Y', null, null); @@ -238,6 +364,7 @@ public final class FrenchRepublicanCalendar * @see #hasSansculottides() * @see #hasMonth() */ + @FormattableElement(format = "S") public static final ChronoElement SANSCULOTTIDES = SANSCULOTTIDES_ACCESS; /** @@ -324,6 +451,7 @@ public final class FrenchRepublicanCalendar * @see #hasSansculottides() * @see #hasMonth() */ + @FormattableElement(format = "C") public static final ChronoElement DAY_OF_DECADE = DAY_OF_DECADE_ACCESS; /** @@ -348,9 +476,9 @@ public final class FrenchRepublicanCalendar * @see #hasSansculottides() * @see #hasMonth() */ - @FormattableElement(format = "d") + @FormattableElement(format = "D") public static final StdCalendarElement DAY_OF_MONTH = - new StdIntegerDateElement<>("DAY_OF_MONTH", FrenchRepublicanCalendar.class, 1, 30, 'd'); + new StdIntegerDateElement<>("DAY_OF_MONTH", FrenchRepublicanCalendar.class, 1, 30, 'D'); /** *

Represents the day of year.

@@ -358,9 +486,8 @@ public final class FrenchRepublicanCalendar /*[deutsch] *

Repräsentiert den Tag des Jahres.

*/ - @FormattableElement(format = "D") public static final StdCalendarElement DAY_OF_YEAR = - new StdIntegerDateElement<>("DAY_OF_YEAR", FrenchRepublicanCalendar.class, 1, 365, 'D'); + new StdIntegerDateElement<>("DAY_OF_YEAR", FrenchRepublicanCalendar.class, 1, 365, '\u0000'); /** *

Represents the day of week where the week is seven days long.

@@ -1838,6 +1965,13 @@ private static class DayOfDecadeAccess //~ Methoden ------------------------------------------------------ + @Override + public char getSymbol() { + + return 'C'; + + } + @Override public DayOfDecade getValue(FrenchRepublicanCalendar context) { @@ -2008,6 +2142,13 @@ private static class SansculottidesAccess //~ Methoden ------------------------------------------------------ + @Override + public char getSymbol() { + + return 'S'; + + } + @Override public Sansculottides getValue(FrenchRepublicanCalendar context) { diff --git a/calendar/src/main/resources/names/extra/frenchrev.properties b/calendar/src/main/resources/names/extra/frenchrev.properties index d681c84b8..bcd3e27e4 100644 --- a/calendar/src/main/resources/names/extra/frenchrev.properties +++ b/calendar/src/main/resources/names/extra/frenchrev.properties @@ -27,6 +27,19 @@ M(w)_10=10 M(w)_11=11 M(w)_12=12 +M(a)_1=01 +M(a)_2=02 +M(a)_3=03 +M(a)_4=04 +M(a)_5=05 +M(a)_6=06 +M(a)_7=07 +M(a)_8=08 +M(a)_9=09 +M(a)_10=10 +M(a)_11=11 +M(a)_12=12 + M(N)_1=1 M(N)_2=2 M(N)_3=3 diff --git a/calendar/src/test/java/net/time4j/calendar/frenchrev/FormatTest.java b/calendar/src/test/java/net/time4j/calendar/frenchrev/FormatTest.java new file mode 100644 index 000000000..a8835de16 --- /dev/null +++ b/calendar/src/test/java/net/time4j/calendar/frenchrev/FormatTest.java @@ -0,0 +1,98 @@ +package net.time4j.calendar.frenchrev; + +import net.time4j.PlainDate; +import net.time4j.engine.CalendarDays; +import net.time4j.format.Attributes; +import net.time4j.format.NumberSystem; +import net.time4j.format.expert.ChronoFormatter; +import net.time4j.format.expert.PatternType; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.text.ParseException; +import java.util.Locale; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + + +@RunWith(JUnit4.class) +public class FormatTest { + + @Test + public void printMonthOrSansculottidesOnGroundLevel() { + ChronoFormatter f = create("D MMMM' an 'Y|SSSS', an 'Y"); + FrenchRepublicanCalendar cal = PlainDate.of(2018, 9, 23).transform(FrenchRepublicanCalendar.axis()); + assertThat( + f.format(cal), + is("1 vendémiaire an 227")); + assertThat( + f.format(cal.minus(CalendarDays.ONE)), + is("jour de la révolution, an 226")); + } + + @Test + public void printMonthOrSansculottidesInsideOptionalSection() { + ChronoFormatter f = create("[D MMMM|SSSS]', an 'Y"); + FrenchRepublicanCalendar cal = PlainDate.of(2018, 9, 23).transform(FrenchRepublicanCalendar.axis()); + assertThat( + f.format(cal), + is("1 vendémiaire, an 227")); + assertThat( + f.format(cal.minus(CalendarDays.ONE)), + is("jour de la révolution, an 226")); + } + + @Test + public void parseMonthOrSansculottides() throws ParseException { + ChronoFormatter f = create("D MMMM' an 'Y|SSSS', an 'Y"); + FrenchRepublicanCalendar cal = PlainDate.of(2018, 9, 23).transform(FrenchRepublicanCalendar.axis()); + assertThat( + f.parse("1 vendémiaire an 227"), + is(cal)); + assertThat( + f.parse("jour de la révolution, an 226"), + is(cal.minus(CalendarDays.ONE))); + } + + @Test + public void printWithSpecificAlgorithm() { + ChronoFormatter f = create("YYYY-MM-DD"); + FrenchRepublicanCalendar cal = PlainDate.of(2018, 9, 23).transform(FrenchRepublicanCalendar.axis()); + assertThat( + f.format(cal), + is("0227-01-01")); + assertThat( + f.with(FrenchRepublicanAlgorithm.attribute(), FrenchRepublicanAlgorithm.EQUINOX).format(cal), + is("0227-01-01")); + assertThat( + f.with(FrenchRepublicanAlgorithm.attribute(), FrenchRepublicanAlgorithm.ROMME).format(cal), + is("0227-01-02")); + } + + @Test + public void parseWithSpecificAlgorithm() throws ParseException { + ChronoFormatter f = create("YYYY-MM-DD"); + FrenchRepublicanCalendar cal = PlainDate.of(2018, 9, 23).transform(FrenchRepublicanCalendar.axis()); + assertThat( + f.parse("0227-01-01"), + is(cal)); + assertThat( + f.with(FrenchRepublicanAlgorithm.attribute(), FrenchRepublicanAlgorithm.EQUINOX).parse("0227-01-01"), + is(cal)); + assertThat( + f.with(FrenchRepublicanAlgorithm.attribute(), FrenchRepublicanAlgorithm.ROMME).parse("0227-01-02"), + is(cal)); + } + + private static ChronoFormatter create(String pattern) { + return ChronoFormatter + .setUp(FrenchRepublicanCalendar.axis(), Locale.FRENCH) + .startSection(Attributes.NUMBER_SYSTEM, NumberSystem.ARABIC) + .addPattern(pattern, PatternType.DYNAMIC) + .endSection() + .build(); + } + +} diff --git a/calendar/src/test/java/net/time4j/calendar/frenchrev/FrenchRepublicanElementTest.java b/calendar/src/test/java/net/time4j/calendar/frenchrev/FrenchRepublicanElementTest.java index bdc97169b..36d0165ed 100644 --- a/calendar/src/test/java/net/time4j/calendar/frenchrev/FrenchRepublicanElementTest.java +++ b/calendar/src/test/java/net/time4j/calendar/frenchrev/FrenchRepublicanElementTest.java @@ -27,7 +27,7 @@ public void dayOfMonth() { is(true)); assertThat( FrenchRepublicanCalendar.DAY_OF_MONTH.getSymbol(), - is('d')); + is('D')); assertThat( FrenchRepublicanCalendar.DAY_OF_MONTH.isDateElement(), is(true)); @@ -119,7 +119,7 @@ public void dayOfDecade() { is(true)); assertThat( FrenchRepublicanCalendar.DAY_OF_DECADE.getSymbol(), - is('\u0000')); + is('C')); assertThat( FrenchRepublicanCalendar.DAY_OF_DECADE.isDateElement(), is(true)); @@ -276,7 +276,7 @@ public void dayOfYear() { is(true)); assertThat( FrenchRepublicanCalendar.DAY_OF_YEAR.getSymbol(), - is('D')); + is('\u0000')); assertThat( FrenchRepublicanCalendar.DAY_OF_YEAR.isDateElement(), is(true)); @@ -598,7 +598,7 @@ public void yearOfEra() { is(true)); assertThat( FrenchRepublicanCalendar.YEAR_OF_ERA.getSymbol(), - is('y')); + is('Y')); assertThat( FrenchRepublicanCalendar.YEAR_OF_ERA.isDateElement(), is(true)); @@ -685,7 +685,7 @@ public void sansculottides() { is(true)); assertThat( FrenchRepublicanCalendar.SANSCULOTTIDES.getSymbol(), - is('\u0000')); + is('S')); assertThat( FrenchRepublicanCalendar.SANSCULOTTIDES.isDateElement(), is(true)); diff --git a/calendar/src/test/java/net/time4j/calendar/frenchrev/FrenchRepublicanSuite.java b/calendar/src/test/java/net/time4j/calendar/frenchrev/FrenchRepublicanSuite.java index 0b3b8f08b..7432f3d07 100644 --- a/calendar/src/test/java/net/time4j/calendar/frenchrev/FrenchRepublicanSuite.java +++ b/calendar/src/test/java/net/time4j/calendar/frenchrev/FrenchRepublicanSuite.java @@ -9,6 +9,7 @@ @SuiteClasses( { DayOfDecadeTest.class, + FormatTest.class, FrenchRepublicanCalendarTest.class, FrenchRepublicanElementTest.class, FrenchRepublicanEraTest.class,