jfxtras.icalendarfx.properties.component.recurrence.rrule.byxxx.ByMonthDay Maven / Gradle / Ivy
package jfxtras.icalendarfx.properties.component.recurrence.rrule.byxxx;
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
import jfxtras.icalendarfx.properties.component.recurrence.rrule.byxxx.ByMonthDay;
import jfxtras.icalendarfx.properties.component.recurrence.rrule.byxxx.ByRuleIntegerAbstract;
import jfxtras.icalendarfx.utilities.DateTimeUtilities;
/**
* By Month Day
* BYMONTHDAY
* RFC 5545, iCalendar 3.3.10, page 42
*
* The BYMONTHDAY rule part specifies a COMMA-separated list of days
of the month. Valid values are 1 to 31 or -31 to -1. For
example, -10 represents the tenth to the last day of the month.
The BYMONTHDAY rule part MUST NOT be specified when the FREQ rule
part is set to WEEKLY.
*
* @author David Bal
*
* */
public class ByMonthDay extends ByRuleIntegerAbstract
{
/** sorted array of days of month
* (i.e. 5, 10 = 5th and 10th days of the month, -3 = 3rd from last day of month)
* Uses a varargs parameter to allow any number of days
*/
/*
* CONSTRUCTORS
*/
public ByMonthDay()
{
super();
}
public ByMonthDay(Integer... daysOfMonth)
{
super(daysOfMonth);
}
public ByMonthDay(ByMonthDay source)
{
super(source);
}
@Override
Predicate isValidValue()
{
return (value) -> (value >= -31) && (value <= 31) && (value != 0);
}
/**
* Return stream of valid dates made by rule (infinite if COUNT or UNTIL not present)
*/
@Override
public Stream streamRecurrences(Stream inStream, ChronoUnit chronoUnit, Temporal dateTimeStart)
{
switch (chronoUnit)
{
case HOURS:
case MINUTES:
case SECONDS:
case DAYS:
return inStream.filter(d ->
{ // filter out all but qualifying days
int myDay = d.get(ChronoField.DAY_OF_MONTH);
int myDaysInMonth = LocalDate.from(d).lengthOfMonth();
for (int day : getValue())
{
if (myDay == day) return true;
// negative daysOfMonth (-3 = 3rd to last day of month)
if ((day < 0) && (myDay == myDaysInMonth + day + 1)) return true;
}
return false;
});
case YEARS:
return inStream.flatMap(d ->
{ // Expand to be daysOfMonth days in current month
List dates = new ArrayList<>();
for (Month month : Month.values())
{
Temporal monthAdjustedTemporal = d.with(ChronoField.MONTH_OF_YEAR, month.getValue());
dates.addAll(extracted(monthAdjustedTemporal, dateTimeStart));
}
return dates.stream().sorted(DateTimeUtilities.TEMPORAL_COMPARATOR);
});
case MONTHS:
return inStream.flatMap(d ->
{ // Expand to be daysOfMonth days in current month
List dates = new ArrayList<>();
dates.addAll(extracted(d, dateTimeStart));
return dates.stream().sorted(DateTimeUtilities.TEMPORAL_COMPARATOR);
});
case WEEKS:
throw new IllegalArgumentException(name().toString() + " is not available for " + chronoUnit + " frequency."); // Not available
default:
throw new IllegalArgumentException("Not implemented: " + chronoUnit);
}
}
/* process dayOfMonth for YEARS and MONTHS */
private List extracted(Temporal initialTemporal, Temporal dateTimeStart)
{
List dates = new ArrayList<>();
for (int dayOfMonth : getValue())
{
final Temporal correctMonthTemporal = (dayOfMonth > 0) ? initialTemporal : initialTemporal.minus(1, ChronoUnit.MONTHS);
int daysInMonth = (int) ChronoUnit.DAYS.between(correctMonthTemporal.with(TemporalAdjusters.firstDayOfMonth()),
correctMonthTemporal.with(TemporalAdjusters.firstDayOfNextMonth()));
int finalDayOfMonth = 0;
if (dayOfMonth > 0)
{
if (dayOfMonth <= daysInMonth)
{
finalDayOfMonth = dayOfMonth;
}
} else if (dayOfMonth < 0)
{
int newDayOfMonth = daysInMonth + dayOfMonth + 1;
if (newDayOfMonth > 0)
{
finalDayOfMonth = newDayOfMonth;
}
} else
{
throw new IllegalArgumentException(name().toString() + " can't have a value of zero");
}
Temporal newTemporal = (finalDayOfMonth != 0) ? correctMonthTemporal.with(ChronoField.DAY_OF_MONTH, finalDayOfMonth) : null;
// ensure day of month hasn't changed. If it changed the date was invalid and should be ignored.
if (newTemporal != null)
{
dates.add(newTemporal);
}
}
return dates;
}
public static ByMonthDay parse(String content)
{
return ByMonthDay.parse(new ByMonthDay(), content);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy