diff --git a/Ical.Net.Tests/RecurrenceTests.cs b/Ical.Net.Tests/RecurrenceTests.cs index 10fc184cf..b3cd1b8a6 100644 --- a/Ical.Net.Tests/RecurrenceTests.cs +++ b/Ical.Net.Tests/RecurrenceTests.cs @@ -108,7 +108,7 @@ public void EventOccurrenceTest( """; var cal = Calendar.Load(calendarIcalStr); - var tzid = cal.Events.Single().Start.TzId; + var tzid = cal.Events.Single().Start?.TzId; var periodSerializer = new PeriodSerializer(); var periods = expectedPeriods @@ -140,7 +140,7 @@ public void YearlyComplex1() while (dt.Year < 2011) { - if (dt.GreaterThan(evt.Start) && + if (dt.GreaterThan(evt.Start!) && (dt.Year % 2 == 1) && // Every-other year from 2005 (dt.Month == 1) && (dt.DayOfWeek == DayOfWeek.Sunday)) @@ -203,7 +203,7 @@ public void DailyUntil1() var i = 0; while (dt.Year < 1998) { - if (dt.GreaterThanOrEqual(evt.Start) && + if (dt.GreaterThanOrEqual(evt.Start!) && dt.LessThan(new CalDateTime(1997, 12, 24, 0, 0, 0, _tzid))) { Assert.Multiple(() => @@ -375,7 +375,7 @@ public void ByMonth1() var i = 0; while (dt.Year < 2001) { - if (dt.GreaterThanOrEqual(evt.Start) && + if (dt.GreaterThanOrEqual(evt.Start!) && dt.Month == 1 && dt.LessThanOrEqual(new CalDateTime(2000, 1, 31, 9, 0, 0, _tzid))) { @@ -2337,7 +2337,7 @@ public void Yearly1() public void Bug2912657() { var iCal = Calendar.Load(IcsFiles.Bug2912657); - var localTzid = iCal.Events.First().Start.TzId; + var localTzid = iCal.Events.First().Start?.TzId; // Daily recurrence EventOccurrenceTest( @@ -2602,7 +2602,7 @@ public void DurationOfRecurrencesOverDst(string dtStart, string dtEnd, string? d for (var index = 0; index < expectedPeriods.Length; index++) { var p = expectedPeriods[index]; - var newStart = p.StartTime.ToTimeZone(start.TzId); + var newStart = p.StartTime.ToTimeZone(start?.TzId); expectedPeriods[index] = Period.Create(newStart, end: newStart.Add(p.Duration!.Value)); } @@ -2866,8 +2866,8 @@ public void UsHolidays() Assert.That(evt, Is.Not.Null); Assert.Multiple(() => { - Assert.That(items.ContainsKey(evt.Summary), Is.True, "Holiday text '" + evt.Summary + "' did not match known holidays."); - Assert.That(o.Period.StartTime, Is.EqualTo(items[evt.Summary]), "Date/time of holiday '" + evt.Summary + "' did not match."); + Assert.That(items.ContainsKey(evt.Summary!), Is.True, "Holiday text '" + evt.Summary + "' did not match known holidays."); + Assert.That(o.Period.StartTime, Is.EqualTo(items[evt.Summary!]), "Date/time of holiday '" + evt.Summary + "' did not match."); }); } } @@ -3904,7 +3904,7 @@ public void TestDtStartTimezone(string? tzId) var cal = Calendar.Load(icalText); var evt = cal.Events.First(); var ev = new EventEvaluator(evt); - var occurrences = ev.Evaluate(evt.DtStart, evt.DtStart.ToTimeZone(tzId), evt.DtStart.AddMinutes(61).ToTimeZone(tzId), null); + var occurrences = ev.Evaluate(evt.DtStart!, evt.DtStart!.ToTimeZone(tzId), evt.DtStart.AddMinutes(61).ToTimeZone(tzId), null); var occurrencesStartTimes = occurrences.Select(x => x.StartTime).Take(2).ToList(); var expectedStartTimes = new[] diff --git a/Ical.Net/CalendarComponents/Alarm.cs b/Ical.Net/CalendarComponents/Alarm.cs index 83ac2f06c..d47aad584 100644 --- a/Ical.Net/CalendarComponents/Alarm.cs +++ b/Ical.Net/CalendarComponents/Alarm.cs @@ -91,7 +91,7 @@ public virtual IList GetOccurrences(IRecurringComponent rc, Cal // Ensure that "FromDate" has already been set if (fromDate == null) { - fromDate = rc.Start.Copy(); + fromDate = rc.Start?.Copy(); } Duration? duration = null; diff --git a/Ical.Net/CalendarComponents/CalendarComponent.cs b/Ical.Net/CalendarComponents/CalendarComponent.cs index 408bb4d03..bc16da19a 100644 --- a/Ical.Net/CalendarComponents/CalendarComponent.cs +++ b/Ical.Net/CalendarComponents/CalendarComponent.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System.Diagnostics; using System.Runtime.Serialization; @@ -18,7 +19,7 @@ public class CalendarComponent : CalendarObject, ICalendarComponent /// /// Returns a list of properties that are associated with the iCalendar object. /// - public virtual CalendarPropertyList Properties { get; protected set; } + public virtual CalendarPropertyList Properties { get; protected set; } = null!; public CalendarComponent() : base() { @@ -47,8 +48,7 @@ public override void CopyFrom(ICopyable obj) { base.CopyFrom(obj); - var c = obj as ICalendarComponent; - if (c == null) + if (obj is not ICalendarComponent c) { return; } @@ -78,4 +78,4 @@ public virtual void AddProperty(ICalendarProperty p) p.Parent = this; Properties.Set(p.Name, p.Value); } -} \ No newline at end of file +} diff --git a/Ical.Net/CalendarComponents/CalendarEvent.cs b/Ical.Net/CalendarComponents/CalendarEvent.cs index bc7f43845..e480e6b69 100644 --- a/Ical.Net/CalendarComponents/CalendarEvent.cs +++ b/Ical.Net/CalendarComponents/CalendarEvent.cs @@ -174,7 +174,7 @@ public virtual CalDateTime? End /// Returns if the event is an all-day event, /// meaning the is a 'DATE' value type. /// - public virtual bool IsAllDay => !Start.HasTime; + public virtual bool IsAllDay => Start?.HasTime == false; /// /// The geographic location (lat/long) of the event. @@ -346,16 +346,14 @@ public int CompareTo(CalendarEvent? other) { return 1; } - if (DtStart.Equals(other.DtStart)) + if (DtStart is null) { - return 0; + return other.DtStart is null ? 0 : -1; } - if (DtStart.LessThan(other.DtStart)) + if (other.DtStart is null) { - return -1; + return 1; } - - // meaning DtStart is greater than other - return 1; + return DtStart.CompareTo(other.DtStart); } } diff --git a/Ical.Net/CalendarComponents/FreeBusy.cs b/Ical.Net/CalendarComponents/FreeBusy.cs index 5566986ae..6b7659cf0 100644 --- a/Ical.Net/CalendarComponents/FreeBusy.cs +++ b/Ical.Net/CalendarComponents/FreeBusy.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Collections.Generic; using System.Linq; @@ -14,23 +15,23 @@ namespace Ical.Net.CalendarComponents; public class FreeBusy : UniqueComponent, IMergeable { - public static FreeBusy Create(ICalendarObject obj, FreeBusy freeBusyRequest, EvaluationOptions options = default) + public static FreeBusy? Create(ICalendarObject obj, FreeBusy freeBusyRequest, EvaluationOptions? options = null) { - if (!(obj is IGetOccurrencesTyped)) + if ((obj is not IGetOccurrencesTyped occ)) { return null; } - var getOccurrences = (IGetOccurrencesTyped) obj; - var occurrences = getOccurrences.GetOccurrences(freeBusyRequest.Start, freeBusyRequest.End, options); + + var occurrences = occ.GetOccurrences(freeBusyRequest.Start, freeBusyRequest.End, options); var contacts = new List(); var isFilteredByAttendees = false; - if (freeBusyRequest.Attendees != null && freeBusyRequest.Attendees.Count > 0) + if (freeBusyRequest.Attendees.Count > 0) { isFilteredByAttendees = true; var attendees = freeBusyRequest.Attendees .Where(a => a.Value != null) - .Select(a => a.Value.OriginalString.Trim()); + .Select(a => a.Value!.OriginalString.Trim()); contacts.AddRange(attendees); } @@ -41,12 +42,8 @@ public static FreeBusy Create(ICalendarObject obj, FreeBusy freeBusyRequest, Eva foreach (var o in occurrences) { - var uc = o.Source as IUniqueComponent; - - if (uc == null) - { + if (o.Source is not IUniqueComponent uc) continue; - } var evt = uc as CalendarEvent; var accepted = false; @@ -68,8 +65,7 @@ public static FreeBusy Create(ICalendarObject obj, FreeBusy freeBusyRequest, Eva var participatingAttendeeQuery = uc.Attendees .Where(attendee => attendee.Value != null - && contacts.Contains(attendee.Value.OriginalString.Trim()) - && attendee.ParticipationStatus != null) + && contacts.Contains(attendee.Value.OriginalString.Trim())) .Select(pa => pa.ParticipationStatus.ToUpperInvariant()); foreach (var participatingAttendee in participatingAttendeeQuery) @@ -98,7 +94,7 @@ public static FreeBusy Create(ICalendarObject obj, FreeBusy freeBusyRequest, Eva return fb; } - public static FreeBusy CreateRequest(CalDateTime fromInclusive, CalDateTime toExclusive, Organizer organizer, IEnumerable contacts) + public static FreeBusy CreateRequest(CalDateTime fromInclusive, CalDateTime toExclusive, Organizer? organizer, IEnumerable? contacts) { var fb = new FreeBusy { @@ -158,7 +154,7 @@ public virtual CalDateTime End set => Properties.Set("DTEND", value); } - public virtual FreeBusyStatus GetFreeBusyStatus(Period period) + public virtual FreeBusyStatus GetFreeBusyStatus(Period? period) { var status = FreeBusyStatus.Free; if (period == null) @@ -173,7 +169,7 @@ public virtual FreeBusyStatus GetFreeBusyStatus(Period period) return status; } - public virtual FreeBusyStatus GetFreeBusyStatus(CalDateTime dt) + public virtual FreeBusyStatus GetFreeBusyStatus(CalDateTime? dt) { var status = FreeBusyStatus.Free; if (dt == null) @@ -190,7 +186,7 @@ public virtual FreeBusyStatus GetFreeBusyStatus(CalDateTime dt) public virtual void MergeWith(IMergeable obj) { - if (!(obj is FreeBusy fb)) + if (obj is not FreeBusy fb) { return; } diff --git a/Ical.Net/CalendarComponents/IAlarmContainer.cs b/Ical.Net/CalendarComponents/IAlarmContainer.cs index 5e42f5796..222ab2204 100644 --- a/Ical.Net/CalendarComponents/IAlarmContainer.cs +++ b/Ical.Net/CalendarComponents/IAlarmContainer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System.Collections.Generic; using Ical.Net.DataTypes; @@ -16,7 +17,7 @@ public interface IAlarmContainer ICalendarObjectList Alarms { get; } /// - /// Polls s for occurrences within the d + /// Polls s for occurrences within the d /// time frame of this . For each evaluated /// occurrence if this component, each is polled for its /// corresponding alarm occurrences. @@ -24,5 +25,5 @@ public interface IAlarmContainer /// The earliest allowable alarm occurrence to poll, or null. /// /// A List of objects, one for each occurrence of the . - IList PollAlarms(CalDateTime startTime, CalDateTime endTime); -} \ No newline at end of file + IList PollAlarms(CalDateTime? startTime, CalDateTime? endTime); +} diff --git a/Ical.Net/CalendarComponents/ICalendarComponent.cs b/Ical.Net/CalendarComponents/ICalendarComponent.cs index 7df4820f2..227f24618 100644 --- a/Ical.Net/CalendarComponents/ICalendarComponent.cs +++ b/Ical.Net/CalendarComponents/ICalendarComponent.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable namespace Ical.Net.CalendarComponents; -public interface ICalendarComponent : ICalendarPropertyListContainer { } \ No newline at end of file +public interface ICalendarComponent : ICalendarPropertyListContainer { } diff --git a/Ical.Net/CalendarComponents/IRecurrable.cs b/Ical.Net/CalendarComponents/IRecurrable.cs index 1cb1a0865..05ff404a8 100644 --- a/Ical.Net/CalendarComponents/IRecurrable.cs +++ b/Ical.Net/CalendarComponents/IRecurrable.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System.Collections.Generic; using Ical.Net.DataTypes; using Ical.Net.Evaluation; @@ -14,12 +15,12 @@ public interface IRecurrable : IGetOccurrences /// /// Gets/sets the start date/time of the component. /// - CalDateTime Start { get; set; } + CalDateTime? Start { get; set; } ExceptionDates ExceptionDates { get; } IList ExceptionRules { get; set; } RecurrenceDates RecurrenceDates { get; } IList RecurrenceRules { get; set; } - CalDateTime RecurrenceId { get; set; } - IEvaluator Evaluator { get; } + CalDateTime? RecurrenceId { get; set; } + IEvaluator? Evaluator { get; } } diff --git a/Ical.Net/CalendarComponents/IRecurringComponent.cs b/Ical.Net/CalendarComponents/IRecurringComponent.cs index 597659397..9b5690b19 100644 --- a/Ical.Net/CalendarComponents/IRecurringComponent.cs +++ b/Ical.Net/CalendarComponents/IRecurringComponent.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System.Collections.Generic; using Ical.Net.DataTypes; @@ -12,13 +13,13 @@ public interface IRecurringComponent : IUniqueComponent, IRecurrable { IList Attachments { get; set; } IList Categories { get; set; } - string Class { get; set; } + string? Class { get; set; } IList Contacts { get; set; } - CalDateTime Created { get; set; } - string Description { get; set; } - CalDateTime LastModified { get; set; } + CalDateTime? Created { get; set; } + string? Description { get; set; } + CalDateTime? LastModified { get; set; } int Priority { get; set; } IList RelatedComponents { get; set; } int Sequence { get; set; } - string Summary { get; set; } -} \ No newline at end of file + string? Summary { get; set; } +} diff --git a/Ical.Net/CalendarComponents/IUniqueComponent.cs b/Ical.Net/CalendarComponents/IUniqueComponent.cs index e5004b0e8..6f5b3da82 100644 --- a/Ical.Net/CalendarComponents/IUniqueComponent.cs +++ b/Ical.Net/CalendarComponents/IUniqueComponent.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Collections.Generic; using Ical.Net.DataTypes; @@ -11,12 +12,12 @@ namespace Ical.Net.CalendarComponents; public interface IUniqueComponent : ICalendarComponent { - string Uid { get; set; } + string? Uid { get; set; } IList Attendees { get; set; } IList Comments { get; set; } - CalDateTime DtStamp { get; set; } - Organizer Organizer { get; set; } + CalDateTime? DtStamp { get; set; } + Organizer? Organizer { get; set; } IList RequestStatuses { get; set; } - Uri Url { get; set; } -} \ No newline at end of file + Uri? Url { get; set; } +} diff --git a/Ical.Net/CalendarComponents/Journal.cs b/Ical.Net/CalendarComponents/Journal.cs index d6194bc8f..6128a85b3 100644 --- a/Ical.Net/CalendarComponents/Journal.cs +++ b/Ical.Net/CalendarComponents/Journal.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using System.Runtime.Serialization; using Ical.Net.Evaluation; @@ -35,9 +36,17 @@ protected override void OnDeserializing(StreamingContext context) base.OnDeserializing(context); } - protected bool Equals(Journal other) => Start.Equals(other.Start) && Equals(other as RecurringComponent); + protected bool Equals(Journal? other) + { + if (Start == null || other?.Start == null) + { + return Start == other?.Start; // Both must be null to be considered equal + } + + return Start.Equals(other.Start) && Equals(other as RecurringComponent); + } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; diff --git a/Ical.Net/CalendarComponents/RecurringComponent.cs b/Ical.Net/CalendarComponents/RecurringComponent.cs index ca377dd58..24db107e2 100644 --- a/Ical.Net/CalendarComponents/RecurringComponent.cs +++ b/Ical.Net/CalendarComponents/RecurringComponent.cs @@ -41,7 +41,7 @@ public virtual IList Categories set => Properties.Set("CATEGORIES", value); } - public virtual string Class + public virtual string? Class { get => Properties.Get("CLASS"); set => Properties.Set("CLASS", value); @@ -53,13 +53,13 @@ public virtual IList Contacts set => Properties.Set("CONTACT", value); } - public virtual CalDateTime Created + public virtual CalDateTime? Created { get => Properties.Get("CREATED"); set => Properties.Set("CREATED", value); } - public virtual string Description + public virtual string? Description { get => Properties.Get("DESCRIPTION"); set => Properties.Set("DESCRIPTION", value); @@ -68,7 +68,7 @@ public virtual string Description /// /// The start date/time of the component. /// - public virtual CalDateTime DtStart + public virtual CalDateTime? DtStart { get => Properties.Get("DTSTART"); set => Properties.Set("DTSTART", value); @@ -88,7 +88,7 @@ public virtual IList ExceptionRules set => Properties.Set("EXRULE", value); } - public virtual CalDateTime LastModified + public virtual CalDateTime? LastModified { get => Properties.Get("LAST-MODIFIED"); set => Properties.Set("LAST-MODIFIED", value); @@ -114,7 +114,7 @@ public virtual IList RecurrenceRules set => Properties.Set("RRULE", value); } - public virtual CalDateTime RecurrenceId + public virtual CalDateTime? RecurrenceId { get => Properties.Get("RECURRENCE-ID"); set => Properties.Set("RECURRENCE-ID", value); @@ -135,13 +135,13 @@ public virtual int Sequence /// /// An alias to the DTStart field (i.e. start date/time). /// - public virtual CalDateTime Start + public virtual CalDateTime? Start { get => DtStart; set => DtStart = value; } - public virtual string Summary + public virtual string? Summary { get => Properties.Get("SUMMARY"); set => Properties.Set("SUMMARY", value); diff --git a/Ical.Net/CalendarComponents/Todo.cs b/Ical.Net/CalendarComponents/Todo.cs index e39eb7e6a..243cf0d03 100644 --- a/Ical.Net/CalendarComponents/Todo.cs +++ b/Ical.Net/CalendarComponents/Todo.cs @@ -3,7 +3,7 @@ // Licensed under the MIT license. // -using System; +#nullable enable using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -22,18 +22,18 @@ public class Todo : RecurringComponent, IAlarmContainer private readonly TodoEvaluator _mEvaluator; /// - /// The date/time the todo was completed. + /// The date/time when the item was completed. /// - public virtual CalDateTime Completed + public virtual CalDateTime? Completed { get => Properties.Get("COMPLETED"); set => Properties.Set("COMPLETED", value); } /// - /// The due date of the todo item. + /// The due date of the item. /// - public virtual CalDateTime Due + public virtual CalDateTime? Due { get => Properties.Get("DUE"); set @@ -43,7 +43,7 @@ public virtual CalDateTime Due } /// - /// The duration of the todo item. + /// The duration of the item. /// // NOTE: Duration is not supported by all systems, // (i.e. iPhone) and cannot co-exist with Due. @@ -64,13 +64,13 @@ public virtual Duration? Duration } } - public virtual GeographicLocation GeographicLocation + public virtual GeographicLocation? GeographicLocation { get => Properties.Get("GEO"); set => Properties.Set("GEO", value); } - public virtual string Location + public virtual string? Location { get => Properties.Get("LOCATION"); set => Properties.Set("LOCATION", value); @@ -89,7 +89,7 @@ public virtual IList Resources } /// - /// The status of the todo item. + /// The status of the item. /// public virtual string Status { @@ -124,16 +124,16 @@ public Todo() } /// - /// Use this method to determine if a todo item has been completed. + /// Use this method to determine if an item has been completed. /// This takes into account recurrence items and the previous date /// of completion, if any. /// - /// This method evaluates the recurrence pattern for this TODO + /// This method evaluates the recurrence pattern for this item /// as necessary to ensure all relevant information is taken /// into account to give the most accurate result possible. /// /// - /// True if the todo item has been completed + /// True if the item has been completed public virtual bool IsCompleted(CalDateTime currDt) { if (Status == TodoStatus.Completed) @@ -144,7 +144,7 @@ public virtual bool IsCompleted(CalDateTime currDt) } // Evaluate to the previous occurrence. - var periods = _mEvaluator.EvaluateToPreviousOccurrence(Completed, currDt, options: default); + var periods = _mEvaluator.EvaluateToPreviousOccurrence(Completed, currDt, options: null); return periods.All(p => !p.StartTime.GreaterThan(Completed) || !currDt.GreaterThanOrEqual(p.StartTime)); } @@ -152,7 +152,7 @@ public virtual bool IsCompleted(CalDateTime currDt) } /// - /// Returns 'True' if the todo item is Active as of . + /// Returns 'True' if the item is Active as of . /// An item is Active if it requires action of some sort. /// /// The date and time to test. @@ -162,9 +162,9 @@ public virtual bool IsActive(CalDateTime currDt) && (!IsCompleted(currDt) && !IsCancelled); /// - /// Returns True if the todo item was cancelled. + /// Returns True if the item was cancelled. /// - /// True if the todo was cancelled, False otherwise. + /// True if the item was cancelled, False otherwise. public virtual bool IsCancelled => string.Equals(Status, TodoStatus.Cancelled, TodoStatus.Comparison); public override IEvaluator Evaluator => _mEvaluator; diff --git a/Ical.Net/CalendarComponents/UniqueComponent.cs b/Ical.Net/CalendarComponents/UniqueComponent.cs index e4e68fea5..701374ede 100644 --- a/Ical.Net/CalendarComponents/UniqueComponent.cs +++ b/Ical.Net/CalendarComponents/UniqueComponent.cs @@ -3,11 +3,11 @@ // Licensed under the MIT license. // +#nullable enable using System; using System.Collections.Generic; using System.Runtime.Serialization; using Ical.Net.DataTypes; -using Ical.Net.Utility; namespace Ical.Net.CalendarComponents; @@ -61,13 +61,13 @@ public virtual IList Comments set => Properties.Set("COMMENT", value); } - public virtual CalDateTime DtStamp + public virtual CalDateTime? DtStamp { get => Properties.Get("DTSTAMP"); set => Properties.Set("DTSTAMP", value); } - public virtual Organizer Organizer + public virtual Organizer? Organizer { get => Properties.Get("ORGANIZER"); set => Properties.Set("ORGANIZER", value); @@ -79,7 +79,7 @@ public virtual IList RequestStatuses set => Properties.Set("REQUEST-STATUS", value); } - public virtual Uri Url + public virtual Uri? Url { get => Properties.Get("URL"); set => Properties.Set("URL", value); @@ -92,26 +92,25 @@ protected override void OnDeserialized(StreamingContext context) EnsureProperties(); } - public int CompareTo(UniqueComponent other) - => string.Compare(Uid, other.Uid, StringComparison.OrdinalIgnoreCase); + public int CompareTo(UniqueComponent? other) + => string.Compare(Uid, other?.Uid, StringComparison.OrdinalIgnoreCase); - public override bool Equals(object obj) + public override bool Equals(object? obj) { - if (obj is RecurringComponent && obj != this) + if (obj is not RecurringComponent rec || rec == this) + return base.Equals(obj); + + if (Uid != null) { - var r = (RecurringComponent)obj; - if (Uid != null) - { - return Uid.Equals(r.Uid); - } - return Uid == r.Uid; + return Uid.Equals(rec.Uid); } - return base.Equals(obj); + + return Uid == rec.Uid; } public override int GetHashCode() => Uid?.GetHashCode() ?? base.GetHashCode(); - public virtual string Uid + public virtual string? Uid { get => Properties.Get("UID"); set => Properties.Set("UID", value); diff --git a/Ical.Net/CalendarComponents/VTimeZone.cs b/Ical.Net/CalendarComponents/VTimeZone.cs index d563cdd12..26e39164a 100644 --- a/Ical.Net/CalendarComponents/VTimeZone.cs +++ b/Ical.Net/CalendarComponents/VTimeZone.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. // +#nullable enable using Ical.Net.DataTypes; using Ical.Net.Proxies; using Ical.Net.Utility; @@ -83,24 +84,33 @@ public static VTimeZone FromDateTimeZone(string tzId, DateTime earliestDateTimeT } else { - // first, get the latest standard and daylight intervals, find the oldest recurring date in both, set the RRULES for it, and create a VTimeZoneInfos out of them. - //standard + // first, get the latest standard and daylight intervals, find the oldest recurring date in both, + // set the RRULES for it, and create a VTimeZoneInfos out of them. + + // Standard var standardIntervals = intervals.Where(x => x.Savings.ToTimeSpan() == new TimeSpan(0)).ToList(); var latestStandardInterval = standardIntervals.OrderByDescending(x => x.Start).FirstOrDefault(); + + if (latestStandardInterval == null) + { + return vTimeZone; + } + matchingStandardIntervals = GetMatchingIntervals(standardIntervals, latestStandardInterval, true); var latestStandardTimeZoneInfo = CreateTimeZoneInfo(matchingStandardIntervals, intervals); vTimeZone.AddChild(latestStandardTimeZoneInfo); // check to see if there is no active, future daylight savings (ie, America/Phoenix) - if (latestStandardInterval != null && (latestStandardInterval.HasEnd ? latestStandardInterval.End : Instant.MaxValue) != Instant.MaxValue) + if ((latestStandardInterval.HasEnd ? latestStandardInterval.End : Instant.MaxValue) != Instant.MaxValue) { //daylight var daylightIntervals = intervals.Where(x => x.Savings.ToTimeSpan() != new TimeSpan(0)).ToList(); - if (daylightIntervals.Any()) + if (daylightIntervals.Count != 0) { - var latestDaylightInterval = daylightIntervals.OrderByDescending(x => x.Start).FirstOrDefault(); - matchingDaylightIntervals = GetMatchingIntervals(daylightIntervals, latestDaylightInterval, true); + var latestDaylightInterval = daylightIntervals.OrderByDescending(x => x.Start).First(); + matchingDaylightIntervals = + GetMatchingIntervals(daylightIntervals, latestDaylightInterval, true); var latestDaylightTimeZoneInfo = CreateTimeZoneInfo(matchingDaylightIntervals, intervals); vTimeZone.AddChild(latestDaylightTimeZoneInfo); } @@ -136,9 +146,9 @@ public static VTimeZone FromDateTimeZone(string tzId, DateTime earliestDateTimeT private static VTimeZoneInfo CreateTimeZoneInfo(List matchedIntervals, List intervals, bool isRRule = true, bool isOnlyInterval = false) { - if (matchedIntervals == null || !matchedIntervals.Any()) + if (matchedIntervals == null || matchedIntervals.Count == 0) { - throw new ArgumentException("No intervals found in matchedIntervals"); + throw new InvalidOperationException("No intervals found in matchedIntervals"); } var oldestInterval = matchedIntervals.OrderBy(x => x.Start).FirstOrDefault(); @@ -157,7 +167,7 @@ private static VTimeZoneInfo CreateTimeZoneInfo(List matchedInterv } else if (isOnlyInterval) { - delta = new TimeSpan(); + delta = TimeSpan.Zero; } var utcOffset = oldestInterval.StandardOffset.ToTimeSpan(); @@ -242,11 +252,7 @@ private static void PopulateTimeZoneInfoRecurrenceDates(VTimeZoneInfo tzi, List< foreach (var interval in intervals) { var time = interval.IsoLocalStart.ToDateTimeUnspecified(); - var date = new CalDateTime(time, true).Add(delta.ToDurationExact()) as CalDateTime; - if (date == null) - { - continue; - } + var date = new CalDateTime(time, true).Add(delta.ToDurationExact()); tzi.RecurrenceDates.Add(date); } @@ -293,8 +299,8 @@ public VTimeZone(string tzId) : this() Location = _nodaZone.Id; } - private DateTimeZone _nodaZone; - private string _tzId; + private DateTimeZone _nodaZone = DateTimeZone.Utc; // must initialize + private string? _tzId; public virtual string TzId { get @@ -336,8 +342,8 @@ public virtual string TzId } } - private Uri _url; - public virtual Uri Url + private Uri? _url; + public virtual Uri? Url { get => _url ?? (_url = Properties.Get("TZURL")); set @@ -347,10 +353,10 @@ public virtual Uri Url } } - private string _location; - public string Location + private string? _location; + public string? Location { - get => _location ?? (_location = Properties.Get("X-LIC-LOCATION")); + get => _location ??= Properties.Get("X-LIC-LOCATION"); set { _location = value; @@ -365,20 +371,20 @@ protected bool Equals(VTimeZone other) && string.Equals(TzId, other.TzId, StringComparison.OrdinalIgnoreCase) && Equals(Url, other.Url); - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((VTimeZone)obj); + if (obj.GetType() != GetType()) return false; + return Equals((VTimeZone) obj); } public override int GetHashCode() { unchecked { - var hashCode = Name.GetHashCode(); - hashCode = (hashCode * 397) ^ (TzId?.GetHashCode() ?? 0); + var hashCode = Name?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ (TzId.GetHashCode()); hashCode = (hashCode * 397) ^ (Url?.GetHashCode() ?? 0); return hashCode; } diff --git a/Ical.Net/Evaluation/TodoEvaluator.cs b/Ical.Net/Evaluation/TodoEvaluator.cs index ba97d0cb8..6678c267d 100644 --- a/Ical.Net/Evaluation/TodoEvaluator.cs +++ b/Ical.Net/Evaluation/TodoEvaluator.cs @@ -9,7 +9,6 @@ using System.Linq; using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; -using Ical.Net.Utility; namespace Ical.Net.Evaluation; @@ -19,31 +18,30 @@ public class TodoEvaluator : RecurringEvaluator public TodoEvaluator(Todo todo) : base(todo) { } - internal IEnumerable EvaluateToPreviousOccurrence(CalDateTime completedDate, CalDateTime currDt, EvaluationOptions options) + internal IEnumerable EvaluateToPreviousOccurrence(CalDateTime completedDate, CalDateTime currDt, EvaluationOptions? options) { var beginningDate = completedDate.Copy(); - if (Todo.RecurrenceRules != null) + foreach (var rrule in Todo.RecurrenceRules) { - foreach (var rrule in Todo.RecurrenceRules) - { - DetermineStartingRecurrence(rrule, ref beginningDate); - } + DetermineStartingRecurrence(rrule, ref beginningDate); } DetermineStartingRecurrence(Todo.RecurrenceDates.GetAllPeriods(), ref beginningDate); DetermineStartingRecurrence(Todo.RecurrenceDates.GetAllDates(), ref beginningDate); - if (Todo.ExceptionRules != null) + foreach (var exrule in Todo.ExceptionRules) { - foreach (var exrule in Todo.ExceptionRules) - { - DetermineStartingRecurrence(exrule, ref beginningDate); - } + DetermineStartingRecurrence(exrule, ref beginningDate); } DetermineStartingRecurrence(Todo.ExceptionDates.GetAllDates(), ref beginningDate); + if (Todo.Start == null) + { + throw new InvalidOperationException("Todo.Start must not be null."); + } + return Evaluate(Todo.Start, beginningDate, currDt.AddSeconds(1), options); } @@ -67,6 +65,8 @@ private static void DetermineStartingRecurrence(IEnumerable rdate, private void DetermineStartingRecurrence(RecurrencePattern recur, ref CalDateTime referenceDateTime) { + if (Todo.Start is null) return; + if (recur.Count.HasValue) { referenceDateTime = Todo.Start.Copy(); diff --git a/Ical.Net/VTimeZoneInfo.cs b/Ical.Net/VTimeZoneInfo.cs index a9034d37a..6973eba3b 100644 --- a/Ical.Net/VTimeZoneInfo.cs +++ b/Ical.Net/VTimeZoneInfo.cs @@ -47,8 +47,7 @@ protected override void OnDeserializing(StreamingContext context) public override bool Equals(object? obj) { - var tzi = obj as VTimeZoneInfo; - if (tzi != null) + if (obj is VTimeZoneInfo tzi) { return Equals(TimeZoneName, tzi.TimeZoneName) && Equals(OffsetFrom, tzi.OffsetFrom) && @@ -100,44 +99,32 @@ public virtual string? TimeZoneName TimeZoneNames.Add(value ?? string.Empty); } } - - public virtual UtcOffset TZOffsetFrom - { - get => OffsetFrom; - set => OffsetFrom = value; - } - - public virtual UtcOffset OffsetFrom + + public virtual UtcOffset? OffsetFrom { get => Properties.Get("TZOFFSETFROM"); set => Properties.Set("TZOFFSETFROM", value); } - public virtual UtcOffset OffsetTo + public virtual UtcOffset? OffsetTo { get => Properties.Get("TZOFFSETTO"); set => Properties.Set("TZOFFSETTO", value); } - - public virtual UtcOffset TZOffsetTo - { - get => OffsetTo; - set => OffsetTo = value; - } - + public virtual IList TimeZoneNames { get => Properties.GetMany("TZNAME"); set => Properties.Set("TZNAME", value); } - public virtual CalDateTime DtStart + public virtual CalDateTime? DtStart { get => Start; set => Start = value; } - public virtual CalDateTime Start + public virtual CalDateTime? Start { get => Properties.Get("DTSTART"); set => Properties.Set("DTSTART", value); @@ -171,7 +158,7 @@ public virtual IList RecurrenceRules set => Properties.Set("RRULE", value); } - public virtual CalDateTime RecurrenceId + public virtual CalDateTime? RecurrenceId { get => Properties.Get("RECURRENCE-ID"); set => Properties.Set("RECURRENCE-ID", value);