11using System . Text ;
22using System . Text . RegularExpressions ;
3+ using HuaJiBot . NET . Utils ;
34using Ical . Net ;
45using Ical . Net . CalendarComponents ;
56using Ical . Net . DataTypes ;
@@ -8,11 +9,42 @@ namespace HuaJiBot.NET.Plugin.Calendar;
89
910internal static class CalendarExtensions
1011{
11- public class Period ( Ical . Net . DataTypes . Period p )
12+ /// <summary>
13+ /// Wraps an ical.net <see cref="Ical.Net.DataTypes.Period"/> and converts its times to
14+ /// <see cref="DateTimeOffset"/>. The <paramref name="calendarEvent"/> is required to
15+ /// correctly compute <see cref="EndTime"/> because ical.net 5.1.1 does not populate
16+ /// <c>Period.EndTime</c> when using the single-argument <c>GetOccurrences</c> overload.
17+ /// </summary>
18+ public class Period ( Ical . Net . DataTypes . Period p , CalendarEvent ? calendarEvent = null )
1219 {
1320 public DateTimeOffset StartTime { get ; } = p . StartTime . ToLocalNetworkTime ( ) ;
1421
15- public DateTimeOffset EndTime { get ; } = ( p . EndTime ?? p . StartTime ) . ToLocalNetworkTime ( ) ;
22+ // ical.net 5.1.1 does not populate Period.EndTime when using GetOccurrences(startArg).
23+ // Compute the end time using the event duration (End - Start) applied to the occurrence's
24+ // start time. This correctly handles both non-recurring and recurring events.
25+ public DateTimeOffset EndTime { get ; } = ComputeEndTime ( p , calendarEvent ) ;
26+
27+ private static DateTimeOffset ComputeEndTime (
28+ Ical . Net . DataTypes . Period p ,
29+ CalendarEvent ? calendarEvent
30+ )
31+ {
32+ if ( p . EndTime is not null )
33+ return p . EndTime . ToLocalNetworkTime ( ) ;
34+ if ( calendarEvent ? . Start is not null )
35+ {
36+ // Compute occurrence end = occurrence start + event duration
37+ // Using duration instead of ev.End directly ensures recurring events
38+ // get the correct end date for each occurrence.
39+ // When End is null, duration is zero, treating the event as instantaneous.
40+ var duration =
41+ ( calendarEvent . End ? . AsUtc ?? calendarEvent . Start . AsUtc )
42+ - calendarEvent . Start . AsUtc ;
43+ var endUtc = p . StartTime . AsUtc . Add ( duration ) ;
44+ return new DateTimeOffset ( endUtc ) . ToOffset ( NetworkTime . LocalTimeZoneOffset ) ;
45+ }
46+ return p . StartTime . ToLocalNetworkTime ( ) ;
47+ }
1648 }
1749
1850 /// <summary>
@@ -74,7 +106,7 @@ orderby tuple.Period.StartTime ascending //按照开始时间排序
74106 occurrence . Source switch
75107 {
76108 CalendarEvent calendarEvent => (
77- Period : new Period ( occurrence . Period ) ,
109+ Period : new Period ( occurrence . Period , calendarEvent ) ,
78110 calendarEvent
79111 ) ,
80112 _ => throw new ArgumentOutOfRangeException (
0 commit comments