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

RecurrencePatternEvaluator treats DTSTART and UNTIL timezones incorrectly #406

Closed
minichma opened this issue Jun 17, 2018 · 5 comments · Fixed by #704
Closed

RecurrencePatternEvaluator treats DTSTART and UNTIL timezones incorrectly #406

minichma opened this issue Jun 17, 2018 · 5 comments · Fixed by #704
Labels

Comments

@minichma
Copy link
Collaborator

minichma commented Jun 17, 2018

EventEvaluator and RecurrencePatternEvaluator seem to handle DTSTART and UNTIL's timezones incorrectly. It seems, that

  • RecurrencePatternEvaluator doesn't consider the time zone of the start value passed into RecurrencePatternEvaluator.Evaluate or CalendarEvent.GetOccurrences and
  • UNTIL must be specified as UTC (in most cases) according to RFC5545, but is treated as it would have the same time zone as DTSTART.

The following two test cases reproduce the problem (Europe/Vienna has offset UTC+2 in June 2018):

        [Test, Category("Recurrence")]
        public void UntilTimezoneTest()
        {
            var icalText = @"BEGIN:VCALENDAR
BEGIN:VEVENT
UID:ignore
DTSTAMP:20180613T154237Z
DTSTART;TZID=Europe/Vienna:20180612T180000
DTEND;TZID=Europe/Vienna:20180612T181000
RRULE:FREQ=HOURLY;UNTIL=20180612T170000Z
END:VEVENT
END:VCALENDAR
";

            var cal = Calendar.Load(icalText);
            var evt = cal.Events.First();
            var occurrences = evt.GetOccurrences(evt.DtStart);

            var occurrencesStartTimes = occurrences.Select(x => x.Period.StartTime).ToList();
            var expectedStartTimes = new[]
            {
                new CalDateTime(2018, 06, 12, 18, 0, 0, "Europe/Vienna"),
                new CalDateTime(2018, 06, 12, 19, 0, 0, "Europe/Vienna"),
            };

            Assert.IsTrue(expectedStartTimes.SequenceEqual(occurrencesStartTimes));
        }

        [Test, Category("Recurrence")]
        public void DTStartTimezoneTest()
        {
            var icalText = @"BEGIN:VCALENDAR
BEGIN:VEVENT
UID:ignore
DTSTAMP:20180613T154237Z
DTSTART;TZID=Europe/Vienna:20180612T180000
DTEND;TZID=Europe/Vienna:20180612T181000
RRULE:FREQ=HOURLY
END:VEVENT
END:VCALENDAR
";

            var cal = Calendar.Load(icalText);
            var evt = cal.Events.First();
            var ev = new EventEvaluator(evt);
            var startUtc = evt.DtStart.AsUtc;
            var occurrences = ev.Evaluate(evt.DtStart, startUtc, startUtc.AddSeconds(3601), false);
            var occurrencesStartTimes = occurrences.Select(x => x.StartTime).ToList();

            var expectedStartTimes = new[]
            {
                new CalDateTime(2018, 06, 12, 18, 0, 0, "Europe/Vienna"),
                new CalDateTime(2018, 06, 12, 19, 0, 0, "Europe/Vienna"),
            };

            Assert.IsTrue(expectedStartTimes.SequenceEqual(occurrencesStartTimes));
        }

(The problem was observed on a device running in the Europe/Vienna timezone. Not sure, whether this has any relevance to the outcome.)

[Edit]: Might be related to #197

@minichma
Copy link
Collaborator Author

@axunonb Problem still exists.

@axunonb
Copy link
Collaborator

axunonb commented Oct 14, 2024

See discussion on PR #574

@minichma
Copy link
Collaborator Author

Not sure I understand the connection to #574. I think this issue a different one and we should reopen it. Probably related to #197, but that's to be shown.

@axunonb
Copy link
Collaborator

axunonb commented Dec 21, 2024

Presently, it's the DTStartTimezoneTest() mentioned that still fails.

@axunonb axunonb added the bug label Jan 11, 2025
@axunonb
Copy link
Collaborator

axunonb commented Jan 11, 2025

If we really wanted to stick to DateTime? as arguments, the fix to make DTStartTimezoneTest()work was this change in EventEvaluator:

public override IEnumerable<Period> Evaluate(IDateTime referenceTime, DateTime? periodStart, DateTime? periodEnd, bool includeReferenceDateInResults)
{
    if (periodStart is { Kind: DateTimeKind.Utc })
        periodStart = new CalDateTime(periodStart.Value, "UTC").ToTimeZone(referenceTime.TzId).Value;

    if (periodEnd is { Kind: DateTimeKind.Utc })
        periodEnd = new CalDateTime(periodEnd.Value, "UTC").ToTimeZone(referenceTime.TzId).Value;
    ... oterh code

Looks like at some time there was awareness of the problem with DateTime arguments. Evaluator contains this method, that is never used with the current code base:

protected IDateTime ConvertToIDateTime(DateTime dt, IDateTime referenceDate)
{
    IDateTime newDt = new CalDateTime(dt, referenceDate.TzId);
    newDt.AssociateWith(referenceDate);
    return newDt;
}

UntilTimezoneTest() already works.

minichma added a commit that referenced this issue Jan 22, 2025
minichma added a commit that referenced this issue Jan 23, 2025
minichma added a commit that referenced this issue Jan 23, 2025
…e cleanup (#704)

* Remove obsolete `IEvaluator.AssociatedObject`

* Remove obsolete `Evaluator.ConvertToIDateTime()`

* Enable `#nullable` for `Evaluator` classes.

* Change `IEvaluator.Evaluate()` start/end params from `DateTime` to `IDateTime` to avoid ambiguity.

* Add test `TestDtStartTimezone` from #406.

* CalDateTime: Disallow initializing from `DateTimeKind.Local`.

* Avoid using `DateTimeKind.Local` in test cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants