Bug 958974 - Monthly rules with more BYDAYs are not always displayed correctly in the first month. r=philipp

This commit is contained in:
Decathlon 2015-02-20 09:09:46 +01:00
Родитель 05bc96ee9a
Коммит 36c9617c8d
3 изменённых файлов: 131 добавлений и 58 удалений

Просмотреть файл

@ -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" +