RRule rrule, DateValue dtStart, TimeZone tzid) {
assert null != tzid;
assert null != dtStart;
Frequency freq = rrule.getFreq();
Weekday wkst = rrule.getWkSt();
DateValue untilUtc = rrule.getUntil();
int count = rrule.getCount();
int interval = rrule.getInterval();
WeekdayNum[] byDay = rrule.getByDay().toArray(new WeekdayNum[0]);
int[] byMonth = rrule.getByMonth();
int[] byMonthDay = rrule.getByMonthDay();
int[] byWeekNo = rrule.getByWeekNo();
int[] byYearDay = rrule.getByYearDay();
int[] bySetPos = rrule.getBySetPos();
int[] byHour = rrule.getByHour();
int[] byMinute = rrule.getByMinute();
int[] bySecond = rrule.getBySecond();
if (interval <= 0) { interval = 1; }
if (null == wkst) {
wkst = Weekday.MO;
}
// Optimize out BYSETPOS where possible.
if (bySetPos.length != 0) {
switch (freq) {
case HOURLY:
// ;BYHOUR=3,6,9;BYSETPOS=-1,1
// is equivalent to
// ;BYHOUR=3,9
if (byHour.length != 0 && byMinute.length <= 1
&& bySecond.length <= 1) {
byHour = filterBySetPos(byHour, bySetPos);
}
// Handling bySetPos for rules that are more frequent than daily
// tends to lead to large amounts of processor being used before other
// work limiting features can kick in since there many seconds between
// dtStart and where the year limit kicks in.
// There are no known use cases for the use of bySetPos with hourly
// minutely and secondly rules so we just ignore it.
bySetPos = NO_INTS;
break;
case MINUTELY:
// ;BYHOUR=3,6,9;BYSETPOS=-1,1
// is equivalent to
// ;BYHOUR=3,9
if (byMinute.length != 0 && bySecond.length <= 1) {
byMinute = filterBySetPos(byMinute, bySetPos);
}
// See bySetPos handling comment above.
bySetPos = NO_INTS;
break;
case SECONDLY:
// ;BYHOUR=3,6,9;BYSETPOS=-1,1
// is equivalent to
// ;BYHOUR=3,9
if (bySecond.length != 0) {
bySecond = filterBySetPos(bySecond, bySetPos);
}
// See bySetPos handling comment above.
bySetPos = NO_INTS;
break;
default:
}
}
DateValue start = dtStart;
if (bySetPos.length != 0) {
// Roll back till the beginning of the period to make sure that any
// positive indices are indexed properly.
// The actual iterator implementation is responsible for anything
// < dtStart.
switch (freq) {
case YEARLY:
start = dtStart instanceof TimeValue
? new DateTimeValueImpl(start.year(), 1, 1, 0, 0, 0)
: new DateValueImpl(start.year(), 1, 1);
break;
case MONTHLY:
start = dtStart instanceof TimeValue
? new DateTimeValueImpl(start.year(), start.month(), 1, 0, 0, 0)
: new DateValueImpl(start.year(), start.month(), 1);
break;
case WEEKLY:
int d = (7 + wkst.ordinal() - Weekday.valueOf(dtStart).ordinal()) % 7;
start = TimeUtils.add(dtStart, new DateValueImpl(0, 0, -d));
break;
default: break;
}
}