Skip to content

Commit

Permalink
Merge pull request #3 from catull/implement-french-republic-calendar
Browse files Browse the repository at this point in the history
Improvements to ranges, calculation
  • Loading branch information
ledahulevogyre committed Mar 2, 2017
2 parents 56df011 + 9fe06f6 commit f7ac065
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.chrono.AbstractChronology;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.chrono.Era;
Expand All @@ -46,7 +47,6 @@
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.ValueRange;
import java.time.temporal.WeekFields;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
Expand All @@ -55,8 +55,8 @@
*/
public final class FrenchRepublicChronology
extends AbstractNileChronology
implements Serializable {
extends AbstractChronology
implements Serializable {

/**
* Singleton instance for the FrenchRepublic chronology.
Expand All @@ -68,6 +68,10 @@ public final class FrenchRepublicChronology
*/
private static final long serialVersionUID = 7291205177830286973L;

/**
* Empty range
*/
static final ValueRange EMPTY = ValueRange.of(0, 0);
/**
* Range of days of week.
*/
Expand All @@ -77,6 +81,27 @@ public final class FrenchRepublicChronology
*/
static final ValueRange ALIGNED_WOM_RANGE = ValueRange.of(1, 1, 3);

/**
* Range of proleptic-year.
*/
static final ValueRange YEAR_RANGE = ValueRange.of(-999_998, 999_999);
/**
* Range of year.
*/
static final ValueRange YOE_RANGE = ValueRange.of(1, 999_999);
/**
* Range of proleptic month.
*/
static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(-999_998L * 12, 999_999L * 12L + 11);
/**
* Range of months.
*/
static final ValueRange MOY_RANGE = ValueRange.of(1, 13);
/**
* Range of days.
*/
static final ValueRange DOM_RANGE = ValueRange.of(1, 30);

/**
* Private constructor, that is public to satisfy the {@code ServiceLoader}.
* @deprecated Use the singleton {@link #INSTANCE} instead.
Expand All @@ -85,6 +110,22 @@ public final class FrenchRepublicChronology
public FrenchRepublicChronology() {
}

//-----------------------------------------------------------------------
/**
* Checks if the specified year is a leap year.
* <p>
* The proleptic-year is leap if the remainder after division by four equals three.
* This method does not validate the year passed in, and only has a
* well-defined result for years in the supported range.
*
* @param prolepticYear the proleptic-year to check, not validated for range
* @return true if the year is a leap year
*/
@Override
public boolean isLeapYear(long prolepticYear) {
return (prolepticYear & 3) == 3;
}

/**
* Resolve singleton.
*
Expand Down Expand Up @@ -306,12 +347,25 @@ public ChronoZonedDateTime<FrenchRepublicDate> zonedDateTime(Instant instant, Zo

@Override
public ValueRange range(ChronoField field) {
if (field == DAY_OF_WEEK) {
return DOW_RANGE;
} else if (field == ALIGNED_WEEK_OF_MONTH) {
return ALIGNED_WOM_RANGE;
switch (field) {
case DAY_OF_WEEK:
return DOW_RANGE;
case DAY_OF_MONTH:
return DOM_RANGE;
case ALIGNED_WEEK_OF_MONTH:
return ALIGNED_WOM_RANGE;
case MONTH_OF_YEAR:
return MOY_RANGE;
case PROLEPTIC_MONTH:
return PROLEPTIC_MONTH_RANGE;
case YEAR_OF_ERA:
return YOE_RANGE;
case YEAR:
return YEAR_RANGE;
default:
break;
}
return super.range(field);
return field.range();
}

//-----------------------------------------------------------------------
Expand Down
137 changes: 115 additions & 22 deletions src/main/java/org/threeten/extra/chrono/FrenchRepublicDate.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
package org.threeten.extra.chrono;

import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.DAY_OF_WEEK;
import static java.time.temporal.ChronoField.DAY_OF_YEAR;
import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
Expand All @@ -55,9 +54,9 @@
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.time.temporal.WeekFields;
import static org.threeten.extra.chrono.FrenchRepublicChronology.DOW_RANGE;


/**
* A date in the FrenchRepublic calendar system.
Expand All @@ -74,7 +73,7 @@
* identity hash code or use the distinction between equals() and ==.
*/
public final class FrenchRepublicDate
extends AbstractNileDate
extends AbstractDate
implements ChronoLocalDate, Serializable {

/**
Expand All @@ -98,11 +97,15 @@ public final class FrenchRepublicDate
/**
* The month.
*/
private final short month;
private final int month;
/**
* The day.
*/
private final short day;
private final int day;
/**
* Epoch day
*/
private final long epochDay;

//-----------------------------------------------------------------------
/**
Expand Down Expand Up @@ -196,22 +199,74 @@ public static FrenchRepublicDate from(TemporalAccessor temporal) {

@Override
ValueRange rangeAlignedWeekOfMonth() {
return ValueRange.of(1, getMonth() == 13 ? 1 : 3);
return ValueRange.of(getMonth() != 13 ? 1 : 0, getMonth() != 13 ? 3 : 0);
}


@Override
int getDayOfWeek() {
return (int) Math.floorMod(day, 10);
if (month == 13) {
return 0;
}
return Math.floorMod(day, 10);
}

@Override
int lengthOfWeek() {
return 10;
}

@Override
public int lengthOfMonth() {
if (getMonth() != 13) {
return 30;
}
return 0;
}

@Override
int lengthOfYearInMonths() {
return 12;
}

@Override
int getDayOfYear() {
return (getMonth() - 1) * 30 + getDayOfMonth();
}

@Override
public ValueRange range(TemporalField field) {
if (field instanceof ChronoField) {
if (isSupported(field)) {
ChronoField f = (ChronoField) field;
switch (f) {
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
case DAY_OF_WEEK:
return month != 13 ? FrenchRepublicChronology.DOW_RANGE : FrenchRepublicChronology.EMPTY;
case ALIGNED_WEEK_OF_MONTH:
return month != 13 ? ValueRange.of(1, 3) : FrenchRepublicChronology.EMPTY;
case ALIGNED_WEEK_OF_YEAR:
return month != 13 ? ValueRange.of(1, 36) : FrenchRepublicChronology.EMPTY;
case DAY_OF_MONTH:
return month != 13 ? ValueRange.of(1, lengthOfMonth()) : FrenchRepublicChronology.EMPTY;
case DAY_OF_YEAR:
return isLeapYear() ? ValueRange.of(1, 366) : ValueRange.of(1, 365);
//case EPOCH_DAY:
// return ValueRange.of(-999_999, 999_999);
//case ERA:
// return ERA_RANGE;
case MONTH_OF_YEAR:
return ValueRange.of(1, 12);
default:
break;
}
} else {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
}
return super.range(field);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code FrenchRepublicDate} representing a date in the FrenchRepublic calendar
Expand Down Expand Up @@ -245,10 +300,10 @@ static FrenchRepublicDate ofYearDay(int prolepticYear, int dayOfYear) {
*/
static FrenchRepublicDate ofEpochDay(final long epochDay) {
EPOCH_DAY.range().checkValidValue(epochDay, EPOCH_DAY); // validate outer bounds
// use of French Rev. -1 makes leap year at the end of cycle
long frenchRevEpochDayPlus365 = epochDay + EPOCH_DAY_DIFFERENCE + 365;
long cycle = Math.floorDiv(frenchRevEpochDayPlus365, DAYS_PER_CYCLE);
long daysInCycle = Math.floorMod(frenchRevEpochDayPlus365, DAYS_PER_CYCLE);
// use of French Republican year - 1 places leap year at the end of cycle
long epochDays = epochDay + EPOCH_DAY_DIFFERENCE + 365;
long cycle = Math.floorDiv(epochDays, DAYS_PER_CYCLE);
long daysInCycle = Math.floorMod(epochDays, DAYS_PER_CYCLE);
if (daysInCycle == DAYS_PER_CYCLE - 1) {
int year = (int) (cycle * 4 + 3);
return ofYearDay(year, 366);
Expand Down Expand Up @@ -301,8 +356,10 @@ static FrenchRepublicDate create(int prolepticYear, int month, int dayOfMonth) {
*/
private FrenchRepublicDate(int prolepticYear, int month, int dayOfMonth) {
this.prolepticYear = prolepticYear;
this.month = (short) month;
this.day = (short) dayOfMonth;
this.month = month;
this.day = dayOfMonth;
this.epochDay = ((prolepticYear - 1) * 365) + Math.floorDiv(prolepticYear, 4) +
(getDayOfYear() - 1) - EPOCH_DAY_DIFFERENCE;
}

/**
Expand All @@ -315,10 +372,6 @@ private Object readResolve() {
}

//-----------------------------------------------------------------------
@Override
int getEpochDayDifference() {
return EPOCH_DAY_DIFFERENCE;
}

@Override
int getProlepticYear() {
Expand All @@ -335,6 +388,10 @@ int getDayOfMonth() {
return day;
}

private long getProlepticWeek() {
return getProlepticMonth() * 3 + ((getDayOfMonth() - 1) / 10) - 1;
}

@Override
FrenchRepublicDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) {
return resolvePreviousValid(newYear, newMonth, dayOfMonth);
Expand All @@ -357,7 +414,7 @@ public FrenchRepublicChronology getChronology() {
/**
* Gets the era applicable at this date.
* <p>
* The FrenchRepublic calendar system has two eras, 'AM' and 'BEFORE_AM',
* The FrenchRepublic calendar system has two eras, 'REPUBLICAN' and 'BEFORE_REPUBLICAN',
* defined by {@link FrenchRepublicEra}.
*
* @return the era applicable at this date, not null
Expand Down Expand Up @@ -389,6 +446,44 @@ public FrenchRepublicDate plus(long amountToAdd, TemporalUnit unit) {
return (FrenchRepublicDate) super.plus(amountToAdd, unit);
}

/*
*/
@Override
FrenchRepublicDate plusMonths(long months) {
if (months % 12 == 0) {
return (FrenchRepublicDate) plusYears(months / 12);
}
if (month == 13 || 0 == months) {
return this;
}
return (FrenchRepublicDate) super.plusMonths(months);
}

@Override
FrenchRepublicDate plusWeeks(long weeks) {
if (weeks % 3 == 0) {
return plusMonths(weeks / 3);
}
if (month == 13 || 0 == weeks) {
return this;
}
long curEm = getProlepticWeek();
long calcEm = Math.addExact(curEm, weeks) + 1;
int newYear = Math.toIntExact(Math.floorDiv(calcEm, lengthOfYearInMonths() * 3));
int yearWeeks = (int) Math.floorMod(calcEm, lengthOfYearInMonths() * 3);
int weekDays = ((getDayOfMonth() - 1) % lengthOfWeek()) + 1;
int newMonth = yearWeeks / 3 + 1;
return resolvePrevious(newYear, newMonth, (yearWeeks % 3) * lengthOfWeek() + weekDays);
}

@Override
FrenchRepublicDate plusDays(long days) {
if (days % 10 == 0) {
return plusWeeks(days / 10);
}
return (FrenchRepublicDate) super.plusDays(days);
}

@Override
public FrenchRepublicDate minus(TemporalAmount amount) {
return (FrenchRepublicDate) amount.subtractFrom(this);
Expand Down Expand Up @@ -418,8 +513,6 @@ public ChronoPeriod until(ChronoLocalDate endDateExclusive) {

@Override
public long toEpochDay() {
long year = (long) getProlepticYear();
long calendarEpochDay = (year * 365) + Math.floorDiv(year, 4) + (getDayOfYear() - 1);
return calendarEpochDay - 365 - getEpochDayDifference();
return this.epochDay;
}
}
Loading

0 comments on commit f7ac065

Please sign in to comment.