diff --git a/lib/src/main/kotlin/at/bitfire/synctools/icalendar/ICalendarGenerator.kt b/lib/src/main/kotlin/at/bitfire/synctools/icalendar/ICalendarGenerator.kt index 094669a5b..528686582 100644 --- a/lib/src/main/kotlin/at/bitfire/synctools/icalendar/ICalendarGenerator.kt +++ b/lib/src/main/kotlin/at/bitfire/synctools/icalendar/ICalendarGenerator.kt @@ -7,6 +7,7 @@ package at.bitfire.synctools.icalendar import androidx.annotation.VisibleForTesting +import at.bitfire.synctools.util.AndroidTimeUtils.toInstant import at.bitfire.synctools.util.Utils.trimToNull import net.fortuna.ical4j.data.CalendarOutputter import net.fortuna.ical4j.model.Calendar @@ -15,7 +16,6 @@ import net.fortuna.ical4j.model.ComponentList import net.fortuna.ical4j.model.Parameter import net.fortuna.ical4j.model.PropertyContainer import net.fortuna.ical4j.model.PropertyList -import net.fortuna.ical4j.model.TemporalAdapter import net.fortuna.ical4j.model.TimeZoneRegistryFactory import net.fortuna.ical4j.model.component.VEvent import net.fortuna.ical4j.model.component.VTimeZone @@ -68,7 +68,9 @@ class ICalendarGenerator { ical += exception exception.dtStart()?.date?.let { start -> - if (earliestStart == null || TemporalAdapter.isBefore(start, earliestStart)) + val startInstant = runCatching { start.toInstant() }.getOrNull() ?: return@let + val earliestInstant = earliestStart?.let { runCatching { it.toInstant() }.getOrNull() } + if (earliestInstant == null || startInstant < earliestInstant) earliestStart = start } usedTimezoneIds += timeZonesOf(exception) diff --git a/lib/src/test/kotlin/at/bitfire/synctools/icalendar/ICalendarGeneratorTest.kt b/lib/src/test/kotlin/at/bitfire/synctools/icalendar/ICalendarGeneratorTest.kt index 56be7778a..ea4c4c2a6 100644 --- a/lib/src/test/kotlin/at/bitfire/synctools/icalendar/ICalendarGeneratorTest.kt +++ b/lib/src/test/kotlin/at/bitfire/synctools/icalendar/ICalendarGeneratorTest.kt @@ -248,4 +248,27 @@ class ICalendarGeneratorTest { assertTrue(result.isEmpty()) } + @Test + fun `Write event with UTC Instant exception DTSTART does not throw`() { + val iCal = StringWriter() + writer.write(AssociatedEvents( + main = VEvent(propertyListOf( + Uid("UTCTEST"), + DtStart(ZonedDateTime.of(LocalDateTime.parse("2025-08-22T05:30:00"), tzBerlin)), + DtStamp("20250822T000000Z"), + RRule("FREQ=DAILY;COUNT=3") + )), + exceptions = listOf( + VEvent(propertyListOf( + Uid("UTCTEST"), + RecurrenceId(Instant.parse("2025-08-22T03:30:00Z")), + DtStart(Instant.parse("2025-08-22T03:30:00Z")), + DtStamp("20250822T000000Z") + )) + ), + prodId = userAgent + ), iCal) + assertTrue(iCal.toString().contains("BEGIN:VCALENDAR")) + } + } \ No newline at end of file