All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jfxtras.icalendarfx.properties.component.recurrence.rrule.byxxx.ByWeekNumber Maven / Gradle / Ivy

There is a newer version: 17-r1
Show newest version
package jfxtras.icalendarfx.properties.component.recurrence.rrule.byxxx;

import java.time.DayOfWeek;
import java.time.Year;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import jfxtras.icalendarfx.properties.component.recurrence.rrule.RecurrenceRuleValue;
import jfxtras.icalendarfx.properties.component.recurrence.rrule.WeekStart;
import jfxtras.icalendarfx.properties.component.recurrence.rrule.byxxx.ByRuleIntegerAbstract;
import jfxtras.icalendarfx.properties.component.recurrence.rrule.byxxx.ByWeekNumber;
import jfxtras.icalendarfx.utilities.DateTimeUtilities;

/** 
 * By Week Number
 * BYWEEKNO
 * RFC 5545, iCalendar 3.3.10, page 42
 * 
 * The BYWEEKNO rule part specifies a COMMA-separated list of
      ordinals specifying weeks of the year.  Valid values are 1 to 53
      or -53 to -1.  This corresponds to weeks according to week
      numbering as defined in [ISO.8601.2004].  A week is defined as a
      seven day period, starting on the day of the week defined to be
      the week start (see WKST).  Week number one of the calendar year
      is the first week that contains at least four (4) days in that
      calendar year.  This rule part MUST NOT be used when the FREQ rule
      part is set to anything other than YEARLY.  For example, 3
      represents the third week of the year.

         Note: Assuming a Monday week start, week 53 can only occur when
         Thursday is January 1 or if it is a leap year and Wednesday is
         January 1.
   *         
   * @author David Bal
   * 
 * */
public class ByWeekNumber extends ByRuleIntegerAbstract
{    
    private final static int MIN_DAYS_IN_WEEK = 4;
    /** Start of week - default start of week is Monday */
    private DayOfWeek getWeekStart()
    {
    	if (getParent() != null)
    	{
    		WeekStart weekStart = ((RecurrenceRuleValue) getParent()).getWeekStart();
			return (weekStart == null) ? WeekStart.DEFAULT_WEEK_START : weekStart.getValue();
    	} else
    	{
    		return WeekStart.DEFAULT_WEEK_START;
    	}
	}

    /*
     * CONSTRUCTORS
     */
    public ByWeekNumber()
    {
        super();
    }
    
    public ByWeekNumber(Integer...weekNumbers)
    {
        super(weekNumbers);
    }
    
    public ByWeekNumber(ByWeekNumber source)
    {
        super(source);
    }
        
    @Override
    Predicate isValidValue()
    {
        return (value) -> (value >= -53) && (value <= 53) && (value != 0);
    }
    
    @Override
    public Stream streamRecurrences(Stream inStream, ChronoUnit chronoUnit, Temporal dateTimeStart )
    {

        switch (chronoUnit)
        {
        case YEARS:
            WeekFields weekFields = WeekFields.of(getWeekStart(), MIN_DAYS_IN_WEEK);
            Stream outStream = inStream.flatMap(date -> 
            { // Expand to include all days matching week numbers
                List dates = new ArrayList<>();
                for (int weekNumber : getValue())
                {
                    Temporal correctYearTemporal = (weekNumber > 0) ? date : date.minus(1, ChronoUnit.YEARS);
                    correctYearTemporal = correctYearTemporal
                            .with(TemporalAdjusters.firstDayOfYear())
                            .with(TemporalAdjusters.nextOrSame(weekFields.getFirstDayOfWeek())); // get first week entirely in correct year
                    Year correctYear = Year.from(correctYearTemporal);
                    long between = 0;
                    int finalWeekNumber;
                    if (weekNumber > 0)
                    {
                        finalWeekNumber = weekNumber;
                    } else if (weekNumber < 0)
                    {
                        int weeksInYear = 53;
                        Temporal startWeek53 = correctYearTemporal.with(weekFields.weekOfWeekBasedYear(), weeksInYear);
                        Temporal lastDayOfYear = correctYearTemporal.with(TemporalAdjusters.lastDayOfYear());
                         between = ChronoUnit.DAYS.between(startWeek53, lastDayOfYear);
                         int adjustment = (between >= 3) ? 1 : 0;
                        finalWeekNumber = weeksInYear + weekNumber + adjustment;
                    } else
                    {
                        throw new IllegalArgumentException(name().toString() + " can't have a value of zero");
                    }
                    
                    final Temporal startDate;
                    if (finalWeekNumber > 0)
                    {
                        startDate = correctYearTemporal
                                .with(weekFields.weekOfWeekBasedYear(), finalWeekNumber);
                    } else if (between < 0)
                    {
                        startDate = correctYearTemporal.minus(1, ChronoUnit.WEEKS);
                    } else
                    {
                        startDate = null;
                    }
                    if (startDate != null)
                    {
                        IntStream.range(0,7).forEach(days -> 
                        {
                            Temporal newTemporal = startDate.plus(days, ChronoUnit.DAYS);
                            Year myYear = Year.from(newTemporal);
                            if (myYear.equals(correctYear))
                            {
                                dates.add(newTemporal);
                            }
                        });
                    }
                }
                return dates.stream().sorted(DateTimeUtilities.TEMPORAL_COMPARATOR);
            });
            return outStream;
        case DAYS:
        case WEEKS:
        case MONTHS:
        case HOURS:
        case MINUTES:
        case SECONDS:
            throw new IllegalArgumentException("BYWEEKNO is not available for " + chronoUnit + " frequency."); // Not available
        default:
            break;
        }
        return null;    
    }

    public static ByWeekNumber parse(String content)
    {
    	return ByWeekNumber.parse(new ByWeekNumber(), content);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy