diff --git a/src/main/java/com/github/jarnaud/republican/RDate.java b/src/main/java/com/github/jarnaud/republican/RDate.java index 2e90485..b8d1f28 100644 --- a/src/main/java/com/github/jarnaud/republican/RDate.java +++ b/src/main/java/com/github/jarnaud/republican/RDate.java @@ -1,12 +1,14 @@ package com.github.jarnaud.republican; import java.time.LocalDate; +import java.time.Year; +import java.time.temporal.*; import java.util.Objects; /** * A Republican local date. */ -public final class RDate implements Comparable { +public final class RDate implements Comparable, TemporalAccessor { /** * The first day in the Republican calendar (corresponding Republican day would be An 1 Vendemiaire 1). @@ -76,6 +78,14 @@ public RMonth getMonth() { return month; } + /** + * Return the decade of this date. + * In the Republican calendar, a decade is a 10 days period within a month (each month being composed of 3 decades). + *

+ * This is NOT a 10 year period! + * + * @return the decade (1, 2 or 3). + */ public int getDecade() { return decade; } @@ -122,6 +132,12 @@ public int hashCode() { return Objects.hash(year, month, decade, day); } + /** + * Return true if the given date is strictly before this date. + * + * @param date the date. + * @return true if date is before this date, false otherwise. + */ public boolean isBefore(RDate date) { if (year != date.getYear()) return year < date.getYear(); if (month.ordinal() != date.getMonth().ordinal()) return month.ordinal() < date.getMonth().ordinal(); @@ -158,4 +174,53 @@ public RDate plusDays(int daysToAdd) { return new GRConverter().convert(gregorianDate); } + + // Temporal accessor implementation. + + @Override + public boolean isSupported(TemporalField field) { + if (field instanceof ChronoField) { + switch ((ChronoField) field) { + case DAY_OF_MONTH: + case MONTH_OF_YEAR: + case YEAR: + return true; + } + } + return false; + } + + @Override + public ValueRange range(TemporalField field) { + if (field instanceof ChronoField) { + if (isSupported(field)) { + ChronoField f = (ChronoField) field; + switch (f) { + case DAY_OF_MONTH: + return ValueRange.of(1, 30); + case MONTH_OF_YEAR: + return ValueRange.of(1, 13); + case YEAR: + return ValueRange.of(1, Year.MAX_VALUE); + } + } + } + throw new UnsupportedTemporalTypeException("Unsupported field: " + field); + } + + @Override + public long getLong(TemporalField field) { + if (field instanceof ChronoField) { + switch ((ChronoField) field) { + case DAY_OF_MONTH: + return getDay(); + case MONTH_OF_YEAR: + return getMonth().ordinal() + 1; + case YEAR: + return getYear(); + } + } + throw new UnsupportedTemporalTypeException("Unsupported field: " + field); + } + } diff --git a/src/test/java/com/github/jarnaud/republican/RDateTemporalAccessorTest.java b/src/test/java/com/github/jarnaud/republican/RDateTemporalAccessorTest.java new file mode 100644 index 0000000..85a752d --- /dev/null +++ b/src/test/java/com/github/jarnaud/republican/RDateTemporalAccessorTest.java @@ -0,0 +1,43 @@ +package com.github.jarnaud.republican; + +import org.junit.jupiter.api.Test; + +import java.time.Year; +import java.time.temporal.UnsupportedTemporalTypeException; +import java.time.temporal.ValueRange; + +import static java.time.temporal.ChronoField.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Test the behaviour of RDate in regard to its implementation of TemporalAccessor. + */ +public class RDateTemporalAccessorTest { + + @Test + public void testRange_supported() { + assertEquals(ValueRange.of(1, 30), RDate.of(6, RMonth.Floreal, 1).range(DAY_OF_MONTH)); + assertEquals(ValueRange.of(1, 13), RDate.of(6, RMonth.Floreal, 1).range(MONTH_OF_YEAR)); + assertEquals(ValueRange.of(1, Year.MAX_VALUE), RDate.of(6, RMonth.Floreal, 1).range(YEAR)); + } + + @Test + public void testRange_notSupported() { + assertThrows(UnsupportedTemporalTypeException.class, () -> RDate.of(6, RMonth.Floreal, 1).range(CLOCK_HOUR_OF_DAY)); + assertThrows(UnsupportedTemporalTypeException.class, () -> RDate.of(6, RMonth.Floreal, 1).range(ALIGNED_WEEK_OF_YEAR)); + } + + @Test + public void testGetLong_supported() { + assertEquals(1, RDate.of(6, RMonth.Floreal, 1).getLong(DAY_OF_MONTH)); + assertEquals(8, RDate.of(6, RMonth.Floreal, 1).getLong(MONTH_OF_YEAR)); + assertEquals(6, RDate.of(6, RMonth.Floreal, 1).getLong(YEAR)); + } + + @Test + public void testGetLong_notSupported() { + assertThrows(UnsupportedTemporalTypeException.class, () -> RDate.of(6, RMonth.Floreal, 1).getLong(CLOCK_HOUR_OF_DAY)); + assertThrows(UnsupportedTemporalTypeException.class, () -> RDate.of(6, RMonth.Floreal, 1).getLong(ALIGNED_WEEK_OF_YEAR)); + } +} diff --git a/src/test/java/com/github/jarnaud/republican/RDateTest.java b/src/test/java/com/github/jarnaud/republican/RDateTest.java index 019d516..eb7c87e 100644 --- a/src/test/java/com/github/jarnaud/republican/RDateTest.java +++ b/src/test/java/com/github/jarnaud/republican/RDateTest.java @@ -58,7 +58,16 @@ public void testIsSextile() { } @Test - public void testPlusDays() { + public void testPlusDays_success() { assertEquals(RDate.of(1, 1, 1), RDate.of(1, 1, 1).plusDays(0)); + assertEquals(RDate.of(1, 1, 2), RDate.of(1, 1, 1).plusDays(1)); + assertEquals(RDate.of(1, 8, 1), RDate.of(1, 1, 1).plusDays(7 * 30)); + assertEquals(RDate.of(2, 1, 1), RDate.of(1, 1, 1).plusDays(365)); } + + @Test + public void testPlusDays_invalid() { + assertThrows(RuntimeException.class, () -> RDate.of(1, 1, 1).plusDays(-1)); + } + }