-
Notifications
You must be signed in to change notification settings - Fork 248
Fix regression introduced in v5.1.3 #889
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
Conversation
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
|
|
679df02 to
9b90f99
Compare
As pointed out in the review for PR #884 Included unit test to verify
Ical.Net.Tests/RecurrenceTests.cs
Outdated
| BEGIN:VCALENDAR | ||
| VERSION:2.0 | ||
| BEGIN:VEVENT | ||
| DTSTART:20250601 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice test! Maybe set DTSTART to 20250602, so it is in line with the RRULE?
Ical.Net/Evaluation/Evaluator.cs
Outdated
| // When a rule uses BYWEEKNO, recurrence enumeration | ||
| // is based on week numbers relative to the year. | ||
| // So we preserve January 1st when adding years; otherwise, preserve month/day. | ||
| // So we preserve the weekday when using BYWEEKNO and preserve month/day otherwise. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, I'm sorry, I guess I was wrong with my comment in #513. We indeed preserve Jan 1st, not the weekday. So I guess the previous version was better.
Ical.Net/Evaluation/Evaluator.cs
Outdated
| // So we preserve the weekday when using BYWEEKNO and preserve month/day otherwise. | ||
| dt = (pattern.ByWeekNo.Count != 0) | ||
| ? old.AddDays(-old.DayOfYear + 1).AddYears(interval) | ||
| : old.AddYears(interval); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't fully understand this change (from #513) though, particularly the non-weekno case. It seems to be somewhat dangerous to just add an interval to the year, especially on Feb 29, as it might not exist in the next interval. Actually, when adding 1y to 2024-02-29, we end up in 2025-02-28 (just tried). However, when reaching 2028, we still stay on the 28th, even though the year has a 29th. The incorrect monthday doesn't seem to cause an issue, as it is properly handled in RecurrencePatternEvaluator, but as it doesn't seem to be considered anyways, I'd rather recommend setting it to a fixed value (Jan 1) rather than maintaining a 'wrong' month day. But then, do we need this distinction at all? We actually don't have a test that convers this distinction, i.e. when setting it always to Jan 1st, all tests are still green.
Moreover, RecurrencePatternEvaluator relies on the assumption that after incrementing, the new refDate is usually at the first day of an interval. For that reason, the following test case fails with the new behavior:
RRULE:FREQ=YEARLY;BYYEARDAY=1,10;UNTIL=20260105
DTSTART:20250110
INSTANCES:20250110,20260101
see also my comments in GetIntervalLowerLimit()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
recommend setting it to a fixed value (Jan 1)
Fixed with next commit
| // Map BYMONTHDAY entries (positive and negative) to absolute days | ||
| // in the target month, then pick the smallest. This handles cases | ||
| // where BYMONTHDAY mixes positive and negative values (e.g. 2,-2). | ||
| var mappedDays = pattern.ByMonthDay |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting back to https://github.com/ical-org/ical.net/pull/884/files/84eb777d104f900184e58cb7cbc11c6126e17816#r2575960238
I was digging into the code (and git history) to remember, why I introduced this method. The code historically relies on the assumption that after Evaluator.IncrementDate() the new refDate is usually set to the first day of the given interval. For cases where this assumption doesn't hold, this method is intended to return the interval's actual start date (lower limit). So far this was only required for yearly recurrences when using BYWEEKNO, because for yearly recurrences the date was always set to Jan 1 but Week 1 could start in the previous year. In all other cases the code worked without that adjustment, but that's not quite obvious too. At least the time of the day is never set to 0:00, which is compensated by the fact, that we add 1d in GetSearchEndDate() but this is not documented at all and by no means obvious.
For now I'd recommend to set the date to Jan 1st in Evaluator.IncrementDate() in case of yearly recurrences. In this method I recommend to only deal with the special case of yearly/byweekno or alternatively return Jan 1st for the yearly case if byweekno is not used (but not only in BYMONTH is set).
At a later time we might investigate how to improve GetIntervalLowerLimit and GetSearchEndDate. The 1d adjustment in GetSearchEndDate is only intended to compensate for potential DST changes, not for refDate not being set to the beginning of the interval. I feel we should improve this so it is always set to the beginning of the interval, including the time part, and ideally also for the yearly/byweekno case. If we manage to do so, we can get rid of GetIntervalLowerLimit altogether.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only deal with the special case of yearly
Fixed with next commit
Ical.Net.Tests/RecurrenceTests.cs
Outdated
| BEGIN:VCALENDAR | ||
| VERSION:2.0 | ||
| BEGIN:VEVENT | ||
| RRULE:FREQ=YEARLY;BYYEARDAY=-1,1;BYMONTHDAY=-1,1;COUNT=3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests would be great candidates for being included in RecurrenceTestCases.txt, which would safe a lot of boiler-plate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed with next commit
|
@minichma Thanks for the review and taking the time for a deep dive. |
a22762a to
e3adb5b
Compare
Restore former behavior in `Evaluator.IncrementDate`: `case FrequencyType.Yearly: dt = old.AddDays(-old.DayOfYear + 1).AddYears(interval);` `RecurrencePatternEvaluator.GetIntervalLowerLimit` For yearly FREQUENCY but no BYMONTH or BYWEEKNO use the original DTSTART's month for interval boundary, preventing occurrences earlier than the intended range. Move test cases of this PR to `RecurrenceTestCases.txt` Added tests mentioned as failing in the review
e3adb5b to
e07ffca
Compare
|
|
@minichma Thanks! |
|
Urw 🙂 |



Implemented review comments for PR #884 by @minichma
These
RRULEs had no test cases and worked in v5.1.2, but failed in v5.1.3Closes #888
Fixes #890