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

DTSTART is not always the first instance #119

Open
ivopintodasilva opened this issue Jun 19, 2024 · 2 comments
Open

DTSTART is not always the first instance #119

ivopintodasilva opened this issue Jun 19, 2024 · 2 comments

Comments

@ivopintodasilva
Copy link

I found a "tricky" situation where I'm not 100% sure what the correct ouput should be according to the RFC.

I have a rule that:

  • Contains a DTSTART that is on a Thursday: DTSTART;TZID=Europe/Berlin:20240530T200000
  • Happens every Wednesday 3 times: RRULE:FREQ=WEEKLY;COUNT=3;INTERVAL=1;BYDAY=WE

Initially I would expect the recurrences to match only the 3 Wednesdays after the DTSTART date.

However, I noticed that Google Calendar actually generates 4 instances for this same rule:

  • One matching DTSTART
  • Three matching the recurring rule

Then looking a bit more closely at the RFC, it states that the DTSTART should always match the first generated occurrence:

image

But then it's also a bit odd that asking for 3 events (COUNT=3) actually generates 4 instances 😱

Do you have more context on what should be the expected behaviour in this case (according to the RFC)?


I wrote a test to describe the issue:

/// An example where a dtstart is the first instance even if it doesn't fit the rule
#[test]
fn dtstart_is_always_first_instance() {
    let dates = "DTSTART;TZID=Europe/Berlin:20240530T200000\n\
        RRULE:FREQ=WEEKLY;COUNT=3;INTERVAL=1;BYDAY=WE"
        .parse::<RRuleSet>()
        .unwrap()
        .all(u16::MAX)
        .dates;
    common::check_occurrences(
        &dates,
        &[
            "2024-05-30T20:00:00+02:00",
            "2024-06-05T20:00:00+02:00",
            "2024-06-12T20:00:00+02:00",
            "2024-06-19T20:00:00+02:00",
        ],
    );
}
@WhyNotHugo
Copy link
Contributor

WhyNotHugo commented Jun 20, 2024

From https://www.rfc-editor.org/rfc/rfc5545#section-3.8.5.3:

The "DTSTART" property value SHOULD be synchronized with the recurrence rule, if specified.

The example that you're giving ignores this recommendation.

The recurrence set generated with a "DTSTART" property value not synchronized with the recurrence rule is undefined

By ignoring the above recommendation, the result of this combination of DTSTART+RRULE is undefined. E.g.: no specific solution is correct. Although we can comment on which ones we think are better/worse.


Let's rewrite this RRULE and DTSTART into plain English:

We will meet, starting on Thursday 30th, every Wednesday. We will have a total of three meetings.

IMHO, it is ambiguous whether there is also a meeting on the 30th or not. I'd ask the sender to reformulate, in alignment with "The "DTSTART" property value SHOULD be synchronized with the recurrence rule".

However, having a total four is absolutely wrong. Section 3.3.10 is quite explicit about this:

The COUNT rule part defines the number of occurrences at which to range-bound the recurrence. The "DTSTART" property value always counts as the first occurrence.

@ivopintodasilva
Copy link
Author

ivopintodasilva commented Jun 21, 2024

Agree with the ambiguity of the rule and understand your arguments 👍

It does feel like Google has a peculiar way of handling these recurring rules. That brings me a following point then:

In order to replicate Google's behaviour, I started adding the DTSTART date as an RDATE (manually) as well.
This way we always generate a first instance with the same date as DTSTART even if it's not synchronized with the recurrence rule. Then the rule set becomes:

DTSTART;TZID=Europe/Berlin:20240530T200000
RDATE;TZID=Europe/Berlin:20240530T200000
RRULE:FREQ=WEEKLY;COUNT=3;INTERVAL=1;BYDAY=WE

and this gives me the exact same result as Google in this specific case.

BUT, in cases where the the DTSTART is synchronized with the recurrence rule, I get duplicated dates. Here's a test example that fails:

DTSTART;TZID=Europe/Berlin:20240530T200000
RDATE;TZID=Europe/Berlin:20240530T200000
RRULE:FREQ=DAILY;COUNT=3
#[test]
fn issue_119_deduplicate() {
    let dates = "DTSTART;TZID=Europe/Berlin:20240530T200000\nRDATE;TZID=Europe/Berlin:20240530T200000\nRRULE:FREQ=DAILY;COUNT=3"
        .parse::<RRuleSet>()
        .unwrap()
        .all(100)
        .dates;

    common::check_occurrences(
        &dates,
        &[
            "2024-05-30T20:00:00+02:00",
            "2024-05-31T20:00:00+02:00",
            "2024-06-01T20:00:00+02:00",
        ],
    );
}

with result:

---- tests::regression::issue_119_deduplicate stdout ----
Given: [
    "2024-05-30T20:00:00+02:00",
    "2024-05-30T20:00:00+02:00",
    "2024-05-31T20:00:00+02:00",
    "2024-06-01T20:00:00+02:00",
]
Expected: [
    "2024-05-30T20:00:00+02:00",
    "2024-05-31T20:00:00+02:00",
    "2024-06-01T20:00:00+02:00",
]

What's your opinion on deduplicating the results in this case (to guarantee we receive unique dates)?

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

No branches or pull requests

2 participants