ical.net icon indicating copy to clipboard operation
ical.net copied to clipboard

Can't get proper occurences with Until set to UTC

Open ivan-prodanov opened this issue 6 years ago • 5 comments

TLDR: When Start/End time is in a timezone and Until is set in UTC, it is not converted correctly and the last occurence is missed.

Example: Start date is 15th Nov from 14:00 to 15:00 Sofia local time (UTC + 2). Until is set to 22nd Nov at 12:00 (UTC). GetOccurrences returns 7 results instead of 8th. The occurrence at 22nd Nov is not included.

Repro: `
var hourStartUtc = 12; var startDate = new CalDateTime(new DateTime(2018, 11, 15).AddHours(hourStartUtc + 2), "Europe/Sofia");

        var vevent = new CalendarEvent
        {
            Start = startDate,
            End = startDate.AddHours(1)
        };

        var rrule = new RecurrencePattern
        {
            Interval = 1,
            Until = new DateTime(2018, 11, 22, hourStartUtc, 0, 0, DateTimeKind.Utc),
            Frequency = FrequencyType.Daily
        };
        vevent.RecurrenceRules.Add(rrule);

        var occurrences = vevent.GetOccurrences(new DateTime(2018, 11, 1), DateTime.MaxValue);

I found within the codebase that the method ProcessRecurrencePattern(IDateTime referenceDate) attempts to convert the UNTIL value to the timezone of DtStart, but it seems to me that it just creates a CalDateTime from the current UTC value without adjusting the timezone:

`

        if (r.Until != DateTime.MinValue)
        {
            r.Until = DateUtil.MatchTimeZone(referenceDate, new CalDateTime(r.Until, referenceDate.TzId)).Value;
        }

Shouldn't it be new CalDateTime(r.Until, "UTC").ToTimeZone(referenceDate.TzId) provided referenceDate.TzId is not null? This fixes it for me and applies the expected behavior.

ivan-prodanov avatar Nov 21 '18 16:11 ivan-prodanov

Duplicate of or at least related to #406?

minichma avatar Dec 29 '18 20:12 minichma

@ivan-prodanov I'm not sure if it's good to convert the UNTIL value to UTC in all cases. According to RFC 5545 Page 41 the definition of UNTIL is

"... Furthermore, if the "DTSTART" property is specified as a date with local time, then the UNTIL rule part MUST also be specified as a date with local time. If the "DTSTART" property is specified as a date with UTC time or a date with local time and time zone reference, then the UNTIL rule part MUST be specified as a date with UTC time." The local time form is simply a time value that does not contain the UTC designator nor does it reference a time zone (e.g. 11:00 PM) . If you're never receiving events with that date time format I'm sure your're fine with your solution.

I'm having troubles with an UNTIL value in an reccurrence rule too and I fixed it that way:

r.Until = DateUtil.MatchTimeZone(referenceDate, new CalDateTime(r.Until, r.Parameters.Get("TZID"))).Value;

I'm fetching the time zone from the recurrence pattern parameters which contains "UTC" if the UNTIL date is in UTC format and which is null if the UNTIL date is a local time. The new CalDateTime is then either created as UTC or local date and converted to the referenceDate time zone correctly.

McGery avatar May 13 '19 14:05 McGery

Hello, i have the same issue for positive UTC (UTC + 1 in my case)

correcting this :

  if (r.Until != DateTime.MinValue)
    {
        r.Until = DateUtil.MatchTimeZone(referenceDate, new CalDateTime(r.Until, referenceDate.TzId)).Value;
    }

to this :

  if (r.Until != DateTime.MinValue)
    {
        r.Until = DateUtil.MatchTimeZone(referenceDate, new CalDateTime(r.Until, "UTC")).Value;
    }

Seems to resolve the problem, and have no impact on tests with negativ UTC.

Zohnya avatar Mar 19 '20 11:03 Zohnya

@rianjs do you like solution suggested @McGery ? if we create a PR would you accept it ?

siraj-mansour-deltatre avatar Mar 16 '21 01:03 siraj-mansour-deltatre

The issue is not solved yet. To get the last occurrence into the occurrences HashSet I converted calendar event's Start and End values to UTC.

calendarEvent.Start.Value = calendarEvent.Start.AsUtc;
calendarEvent.End.Value = calendarEvent.End.AsUtc;

then GetOccurences has worked as expected.

Calendar calendar = new();
calendar.Events.Add(calendarEvent);
var occurrences = calendar.GetOccurrences(start, end);

DreamTreem avatar Aug 23 '22 13:08 DreamTreem