fix(02): correct DURATION-only event end in expand.ts (BUG 1)
- Replace dtend ?? dtstart with event.endDate which handles DURATION-only VEVENTs - Add positive-duration guard (PT30M / P1D) to both non-recurring and recurring branches - Add single-duration.ics fixture and regression test asserting end > start for DURATION-only events
This commit is contained in:
@@ -207,9 +207,22 @@ export function expandOccurrences(
|
||||
// --- 4. Non-recurring event: single occurrence check ---
|
||||
if (!event.isRecurring()) {
|
||||
if (dtstart.compare(rangeStart) >= 0 && dtstart.compare(rangeEnd) < 0) {
|
||||
const dtend = (vevent.getFirstPropertyValue('dtend') as ICAL.Time | null) ?? dtstart
|
||||
// Use ICAL.Event.endDate which derives end from DTEND, or DTSTART+DURATION, or sensible default.
|
||||
// Do NOT use getFirstPropertyValue('dtend') directly — events with only DURATION set return null,
|
||||
// producing zero-duration occurrences (BUG 1).
|
||||
let occEnd: ICAL.Time = (event.endDate ?? dtstart) as ICAL.Time
|
||||
|
||||
// Positive-duration guard: ensure timed events have non-zero height in Schedule-X.
|
||||
if (!allDay && occEnd.compare(dtstart) <= 0) {
|
||||
occEnd = dtstart.clone()
|
||||
occEnd.addDuration(ICAL.Duration.fromString('PT30M'))
|
||||
} else if (allDay && occEnd.compare(dtstart) <= 0) {
|
||||
occEnd = dtstart.clone()
|
||||
occEnd.addDuration(ICAL.Duration.fromString('P1D'))
|
||||
}
|
||||
|
||||
const start = serializeTime(dtstart, allDay)
|
||||
const end = serializeTime(dtend, allDay)
|
||||
const end = serializeTime(occEnd, allDay)
|
||||
occurrences.push({
|
||||
id: makeOccurrenceId(uid, dtstart),
|
||||
uid,
|
||||
@@ -240,9 +253,18 @@ export function expandOccurrences(
|
||||
|
||||
// Compute occurrence end from event duration
|
||||
const duration = event.duration
|
||||
const occEnd = next.clone()
|
||||
let occEnd = next.clone()
|
||||
occEnd.addDuration(duration)
|
||||
|
||||
// Positive-duration guard: same logic as non-recurring branch above.
|
||||
if (!allDay && occEnd.compare(next) <= 0) {
|
||||
occEnd = next.clone()
|
||||
occEnd.addDuration(ICAL.Duration.fromString('PT30M'))
|
||||
} else if (allDay && occEnd.compare(next) <= 0) {
|
||||
occEnd = next.clone()
|
||||
occEnd.addDuration(ICAL.Duration.fromString('P1D'))
|
||||
}
|
||||
|
||||
const start = serializeTime(next, allDay)
|
||||
const end = serializeTime(occEnd, allDay)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user