Bug 958974 - Monthly rules with more BYDAYs are not always displayed correctly in the first month. r=philipp
This commit is contained in:
Родитель
05bc96ee9a
Коммит
36c9617c8d
|
@ -4647,33 +4647,43 @@ ICAL.RecurIterator = (function() {
|
|||
}
|
||||
|
||||
if (this.rule.freq == "MONTHLY" && this.has_by_data("BYDAY")) {
|
||||
|
||||
var coded_day = this.by_data.BYDAY[this.by_indices.BYDAY];
|
||||
var parts = this.ruleDayOfWeek(coded_day);
|
||||
var pos = parts[0];
|
||||
var dow = parts[1];
|
||||
|
||||
var tempLast = null;
|
||||
var initLast = this.last.clone();
|
||||
var daysInMonth = ICAL.Time.daysInMonth(this.last.month, this.last.year);
|
||||
var poscount = 0;
|
||||
|
||||
if (pos >= 0) {
|
||||
for (this.last.day = 1; this.last.day <= daysInMonth; this.last.day++) {
|
||||
if (this.last.dayOfWeek() == dow) {
|
||||
if (++poscount == pos || pos == 0) {
|
||||
break;
|
||||
}
|
||||
// Check every weekday in BYDAY with relative dow and pos.
|
||||
for (var i in this.by_data.BYDAY) {
|
||||
this.last = initLast.clone();
|
||||
var parts = this.ruleDayOfWeek(this.by_data.BYDAY[i]);
|
||||
var pos = parts[0];
|
||||
var dow = parts[1];
|
||||
var dayOfMonth = this.last.nthWeekDay(dow, pos);
|
||||
|
||||
// If |pos| >= 6, the byday is invalid for a monthly rule.
|
||||
if (pos >= 6 || pos <= -6) {
|
||||
throw new Error("Malformed values in BYDAY part");
|
||||
}
|
||||
|
||||
// If a Byday with pos=+/-5 is not in the current month it
|
||||
// must be searched in the next months.
|
||||
if (dayOfMonth > daysInMonth || dayOfMonth <= 0) {
|
||||
// Skip if we have already found a "last" in this month.
|
||||
if (tempLast && tempLast.month == initLast.month) {
|
||||
continue;
|
||||
}
|
||||
while (dayOfMonth > daysInMonth || dayOfMonth <= 0) {
|
||||
this.increment_month();
|
||||
daysInMonth = ICAL.Time.daysInMonth(this.last.month, this.last.year);
|
||||
dayOfMonth = this.last.nthWeekDay(dow, pos);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pos = -pos;
|
||||
for (this.last.day = daysInMonth; this.last.day != 0; this.last.day--) {
|
||||
if (this.last.dayOfWeek() == dow) {
|
||||
if (++poscount == pos) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.last.day = dayOfMonth;
|
||||
if (!tempLast || this.last.compare(tempLast) < 0) {
|
||||
tempLast = this.last.clone();
|
||||
}
|
||||
}
|
||||
this.last = tempLast.clone();
|
||||
|
||||
//XXX: This feels like a hack, but we need to initialize
|
||||
// the BYMONTHDAY case correctly and byDayAndMonthDay handles
|
||||
|
|
|
@ -812,6 +812,8 @@ static int has_by_data(icalrecur_iterator* impl, enum byrule byrule){
|
|||
|
||||
|
||||
static int expand_year_days(icalrecur_iterator* impl, int year);
|
||||
static int nth_weekday(int dow, int pos, struct icaltimetype t);
|
||||
static void increment_month(icalrecur_iterator* impl);
|
||||
|
||||
|
||||
icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
|
||||
|
@ -1025,52 +1027,58 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule,
|
|||
if(impl->rule.freq == ICAL_MONTHLY_RECURRENCE &&
|
||||
has_by_data(impl,BY_DAY)) {
|
||||
|
||||
int dow = icalrecurrencetype_day_day_of_week(
|
||||
impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);
|
||||
int pos = icalrecurrencetype_day_position(
|
||||
impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);
|
||||
|
||||
int poscount = 0;
|
||||
int days_in_month =
|
||||
icaltime_days_in_month(impl->last.month, impl->last.year);
|
||||
|
||||
if(pos >= 0){
|
||||
/* Count up from the first day pf the month to find the
|
||||
pos'th weekday of dow ( like the second monday. ) */
|
||||
struct icaltimetype tmp_last = icaltime_null_time();
|
||||
struct icaltimetype init_last = impl->last;
|
||||
int days_in_month =
|
||||
icaltime_days_in_month(impl->last.month, impl->last.year);
|
||||
int i, dow, pos, day_of_month;
|
||||
|
||||
for(impl->last.day = 1;
|
||||
impl->last.day <= days_in_month;
|
||||
impl->last.day++){
|
||||
|
||||
if(icaltime_day_of_week(impl->last) == dow){
|
||||
if(++poscount == pos || pos == 0){
|
||||
break;
|
||||
}
|
||||
/* Check every weekday in BYDAY with relative dow and pos. */
|
||||
for (i = 0; impl->by_ptrs[BY_DAY][i] != ICAL_RECURRENCE_ARRAY_MAX; i++) {
|
||||
impl->last = init_last;
|
||||
dow = icalrecurrencetype_day_day_of_week(impl->by_ptrs[BY_DAY][i]);
|
||||
pos = icalrecurrencetype_day_position(impl->by_ptrs[BY_DAY][i]);
|
||||
day_of_month = nth_weekday(dow, pos, impl->last);
|
||||
|
||||
/* If |pos| >= 6, the byday is invalid for a monthly rule */
|
||||
if (pos >= 6 || pos <= -6) {
|
||||
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
|
||||
free(impl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If a Byday with pos=+/-5 is not in the current month it
|
||||
must be searched in the next months. */
|
||||
if (day_of_month > days_in_month || day_of_month <= 0) {
|
||||
/* Skip if we have already found a "last" in this month. */
|
||||
if (!icaltime_is_null_time(tmp_last) && tmp_last.month == init_last.month) {
|
||||
continue;
|
||||
}
|
||||
while (day_of_month > days_in_month || day_of_month <= 0) {
|
||||
impl->last.day = 1;
|
||||
increment_month(impl);
|
||||
days_in_month =
|
||||
icaltime_days_in_month(impl->last.month, impl->last.year);
|
||||
day_of_month = nth_weekday(dow, pos, impl->last);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Count down from the last day pf the month to find the
|
||||
pos'th weekday of dow ( like the second to last monday. ) */
|
||||
pos = -pos;
|
||||
for(impl->last.day = days_in_month;
|
||||
impl->last.day != 0;
|
||||
impl->last.day--){
|
||||
|
||||
if(icaltime_day_of_week(impl->last) == dow){
|
||||
if(++poscount == pos ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
impl->last.day = day_of_month;
|
||||
if (icaltime_is_null_time(tmp_last) ||
|
||||
icaltime_compare(impl->last, tmp_last) < 0) {
|
||||
tmp_last = impl->last;
|
||||
}
|
||||
}
|
||||
|
||||
impl->last = tmp_last;
|
||||
|
||||
if(impl->last.day > days_in_month || impl->last.day == 0){
|
||||
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
|
||||
|
||||
if (impl->last.day > days_in_month || impl->last.day == 0) {
|
||||
icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
|
||||
free(impl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (has_by_data(impl,BY_MONTH_DAY)) {
|
||||
// setup_defaults sets the day to -1 for negative BYMONTHDAY values,
|
||||
// so make sure to re-calculate with days_in_month
|
||||
|
|
|
@ -268,6 +268,61 @@ function test_rules() {
|
|||
expectedDates,
|
||||
false);
|
||||
|
||||
// Bug 958974 - Monthly recurrence every WE, FR and the third MO (monthly with more bydays).
|
||||
// Check the occurrences in the first month until the week with the first monday of the rule.
|
||||
check_recur(createEventFromIcalString("BEGIN:VCALENDAR\nBEGIN:VEVENT\n" +
|
||||
"DESCRIPTION:Repeat Monthly every Wednesday, Friday and the third Monday\n" +
|
||||
"RRULE:FREQ=MONTHLY;COUNT=8;BYDAY=3MO,WE,FR\n" +
|
||||
"DTSTART:20150102T080000Z\n" +
|
||||
"DTEND:20150102T090000Z\n" +
|
||||
"END:VEVENT\nEND:VCALENDAR\n"),
|
||||
["20150102T080000Z", "20150107T080000Z", "20150109T080000Z",
|
||||
"20150114T080000Z", "20150116T080000Z", "20150119T080000Z",
|
||||
"20150121T080000Z", "20150123T080000Z"],
|
||||
false);
|
||||
|
||||
// Bug 419490 - Monthly recurrence, the fifth Saturday starting from February.
|
||||
// Check a monthly rule that specifies a day that is not part of the month
|
||||
// the events starts in.
|
||||
check_recur(createEventFromIcalString("BEGIN:VCALENDAR\nBEGIN:VEVENT\n" +
|
||||
"DESCRIPTION:Repeat Monthly the fifth Saturday\n" +
|
||||
"RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=5SA\n" +
|
||||
"DTSTART:20150202T080000Z\n" +
|
||||
"DTEND:20150202T090000Z\n" +
|
||||
"END:VEVENT\nEND:VCALENDAR\n"),
|
||||
["20150202T080000Z",
|
||||
"20150530T080000Z", "20150829T080000Z", "20151031T080000Z",
|
||||
"20160130T080000Z", "20160430T080000Z", "20160730T080000Z"],
|
||||
false);
|
||||
|
||||
// Bug 419490 - Monthly recurrence, the fifth Wednesday every two months starting from February.
|
||||
// Check a monthly rule that specifies a day that is not part of the month
|
||||
// the events starts in.
|
||||
check_recur(createEventFromIcalString("BEGIN:VCALENDAR\nBEGIN:VEVENT\n" +
|
||||
"DESCRIPTION:Repeat Monthly the fifth Friday every two months\n" +
|
||||
"RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=6;BYDAY=5FR\n" +
|
||||
"DTSTART:20150202T080000Z\n" +
|
||||
"DTEND:20150202T090000Z\n" +
|
||||
"END:VEVENT\nEND:VCALENDAR\n"),
|
||||
["20150202T080000Z",
|
||||
"20151030T080000Z", "20160429T080000Z", "20161230T080000Z",
|
||||
"20170630T080000Z", "20171229T080000Z", "20180629T080000Z"],
|
||||
false);
|
||||
|
||||
// Bugs 419490, 958974 - Monthly recurrence, the 2nd Monday, 5th Wednesday and the 5th to last Saturday every month starting from February.
|
||||
// Check a monthly rule that specifies a day that is not part of the month
|
||||
// the events starts in with positive and negative position along with other byday.
|
||||
check_recur(createEventFromIcalString("BEGIN:VCALENDAR\nBEGIN:VEVENT\n" +
|
||||
"DESCRIPTION:Repeat Monthly the 2nd Monday, 5th Wednesday and the 5th to last Saturday every month\n" +
|
||||
"RRULE:FREQ=MONTHLY;COUNT=7;BYDAY=2MO,-5WE,5SA\n" +
|
||||
"DTSTART:20150401T080000Z\n" +
|
||||
"DTEND:20150401T090000Z\n" +
|
||||
"END:VEVENT\nEND:VCALENDAR\n"),
|
||||
["20150401T080000Z",
|
||||
"20150413T080000Z", "20150511T080000Z", "20150530T080000Z",
|
||||
"20150608T080000Z", "20150701T080000Z", "20150713T080000Z"],
|
||||
false);
|
||||
|
||||
let item, occ1;
|
||||
item = makeEvent("DESCRIPTION:occurrence on day 1 moved between the occurrences " +
|
||||
"on days 2 and 3\n" +
|
||||
|
|
Загрузка…
Ссылка в новой задаче