Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make occurrences in chronological order even if BYMONTHDAY or BYYEARDAY is not in order #666

Merged
merged 2 commits into from
Apr 18, 2024

Conversation

darktrojan
Copy link
Collaborator

The recurrence iterator has a problem where it makes occurrences in the same order as given in BYMONTHDAY or BYYEARDAY, which doesn't make sense - it should always return them in chronological order.

This patch fixes it by pre-ordering the days that can be used when the month or year changes. A day that is valid for one month/year may be different or not valid for the next month/year, so the days need to be recomputed each time.

…AY is not in order

The recurrence iterator has a problem where it makes occurrences in the same order as given in
BYMONTHDAY or BYYEARDAY, which doesn't make sense - it should always return them in chronological
order.

This patch fixes it by pre-ordering the days that can be used when the month or year changes.
A day that is valid for one month/year may be different or not valid for the next month/year, so
the days need to be recomputed each time.
@coveralls
Copy link

coveralls commented Apr 16, 2024

Pull Request Test Coverage Report for Build 8730708550

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 107 of 111 (96.4%) changed or added relevant lines in 1 file are covered.
  • 5 unchanged lines in 1 file lost coverage.
  • Overall coverage decreased (-0.07%) to 97.971%

Changes Missing Coverage Covered Lines Changed/Added Lines %
lib/ical/recur_iterator.js 107 111 96.4%
Files with Coverage Reduction New Missed Lines %
lib/ical/recur_iterator.js 5 89.35%
Totals Coverage Status
Change from base Build 8687534234: -0.07%
Covered Lines: 9318
Relevant Lines: 9494

💛 - Coveralls

throw new Error("Malformed values in BYDAY part");
}
if (this.rule.freq == "MONTHLY") {
if (this.has_by_data("BYDAY")) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Github's UI has failed to figure out that all I've done to this block is indent it. I'm pretty sure the block below (else if (this.has_by_data("BYMONTHDAY")) should also only be used if the rule is monthly.

Comment on lines +319 to 337
// Get a sorted list of days in the starting month that match the rule.
let normalized = this.normalizeByMonthDayRules(
this.last.year,
this.last.month,
this.rule.parts.BYMONTHDAY
).filter(d => d >= this.last.day);

if (normalized.length) {
// There's at least one valid day, use it.
this.last.day = normalized[0];
this.by_data.BYMONTHDAY = normalized;
} else {
// There's no occurrence in this month, find the next valid month.
// The longest possible sequence of skipped months is February-April-June,
// so we might need to call next_month up to three times.
if (!this.next_month() && !this.next_month() && !this.next_month()) {
throw new Error("No possible occurrences");
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've thrown out an earlier hack and just used the valid days in the starting month if there is at least one of them.

Comment on lines +518 to +521
rule = parseInt(rules[ruleIdx], 10);
if (isNaN(rule)) {
throw new Error('Invalid BYMONTHDAY value');
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found that if the rule is a string, weird things happen.

Comment on lines +853 to +859
if (this.has_by_data("BYMONTHDAY")) {
this.by_data.BYMONTHDAY = this.normalizeByMonthDayRules(
this.last.year,
this.last.month,
this.rule.parts.BYMONTHDAY
);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the important bit (and the same code just below) – go back to the rule as specified and recalculate the valid days for the new month. This deals with negative rules and puts them all in order.

Comment on lines +1204 to +1209
let daysInYear = Time.isLeapYear(aYear) ? 366 : 365;
this.days.sort((a, b) => {
if (a < 0) a += daysInYear + 1;
if (b < 0) b += daysInYear + 1;
return a - b;
});
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this does the same for BYYEARDAY rules.

Copy link
Owner

@kewisch kewisch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@darktrojan darktrojan merged commit ae06bf4 into kewisch:main Apr 18, 2024
7 checks passed
@darktrojan darktrojan deleted the sorted-bymonthday-byyearday branch April 18, 2024 00:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants