Skip to content
This repository was archived by the owner on May 26, 2026. It is now read-only.

Commit 614026c

Browse files
authored
Events: add VALUE=DATE to EXDATE for cancelled all-day exceptions (#441)
* Add VALUE=DATE to EXDATE for cancelled all-day exceptions * RecurrenceFieldHandler: test VALUE=DATE for all-day EXDATE
1 parent 573ac55 commit 614026c

3 files changed

Lines changed: 47 additions & 6 deletions

File tree

lib/src/main/kotlin/at/bitfire/synctools/mapping/calendar/AndroidEventHandler.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import net.fortuna.ical4j.model.ParameterList
4242
import net.fortuna.ical4j.model.Property
4343
import net.fortuna.ical4j.model.component.VEvent
4444
import net.fortuna.ical4j.model.parameter.TzId
45+
import net.fortuna.ical4j.model.parameter.Value
4546
import net.fortuna.ical4j.model.property.ExDate
4647
import net.fortuna.ical4j.model.property.ProdId
4748
import net.fortuna.ical4j.model.property.RDate
@@ -166,7 +167,7 @@ class AndroidEventHandler(
166167
// Return ExDate
167168
return if (originalAllDay) {
168169
// .. as date, without time
169-
ExDate(DateList(LocalDate.from(date)))
170+
ExDate(ParameterList(listOf(Value.DATE)), DateList(LocalDate.from(date)))
170171
} else if (date is Instant) {
171172
ExDate(DateList(date))
172173
} else {

lib/src/test/kotlin/at/bitfire/synctools/mapping/calendar/AndroidEventHandlerTest.kt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ import android.provider.CalendarContract.Events
1111
import android.provider.CalendarContract.ExtendedProperties
1212
import androidx.core.content.contentValuesOf
1313
import at.bitfire.dateTimeValue
14+
import at.bitfire.dateValue
1415
import at.bitfire.synctools.icalendar.dtStart
1516
import at.bitfire.synctools.icalendar.recurrenceId
1617
import at.bitfire.synctools.storage.calendar.EventAndExceptions
1718
import at.bitfire.synctools.storage.calendar.EventsContract
18-
import at.bitfire.synctools.util.AndroidTimeUtils
19+
import net.fortuna.ical4j.model.Parameter
1920
import net.fortuna.ical4j.model.Property
2021
import net.fortuna.ical4j.model.TimeZoneRegistryFactory
22+
import net.fortuna.ical4j.model.parameter.Value
2123
import net.fortuna.ical4j.model.property.DtStart
2224
import net.fortuna.ical4j.model.property.ExDate
2325
import net.fortuna.ical4j.model.property.ProdId
@@ -148,6 +150,43 @@ class AndroidEventHandlerTest {
148150
assertTrue(result.exceptions.isEmpty())
149151
}
150152

153+
@Test
154+
fun `mapToVEvents rewrites cancelled all-day exception to EXDATE with VALUE=DATE`() {
155+
val result = handler.mapToVEvents(
156+
eventAndExceptions = EventAndExceptions(
157+
main = Entity(contentValuesOf(
158+
Events.TITLE to "Recurring all-day event with cancelled all-day exception",
159+
Events.DTSTART to 1594056600000L,
160+
Events.EVENT_TIMEZONE to "UTC",
161+
Events.ALL_DAY to 1, // main event is all-day
162+
Events.RRULE to "FREQ=DAILY;COUNT=10"
163+
)),
164+
exceptions = listOf(
165+
Entity(contentValuesOf(
166+
Events.ORIGINAL_INSTANCE_TIME to 1594143000000L,
167+
Events.ORIGINAL_ALL_DAY to 1, // exception is all-day
168+
Events.DTSTART to 1594143000000L,
169+
Events.ALL_DAY to 1,
170+
Events.EVENT_TIMEZONE to "UTC",
171+
Events.STATUS to Events.STATUS_CANCELED
172+
))
173+
)
174+
)
175+
).associatedEvents
176+
val main = result.main!!
177+
assertEquals("Recurring all-day event with cancelled all-day exception", main.summary.value)
178+
assertEquals("FREQ=DAILY;COUNT=10", main.getProperty<RRule<*>>(Property.RRULE).get().value)
179+
180+
// Check that EXDATE has VALUE=DATE
181+
val exDate = main.getProperty<ExDate<*>>(Property.EXDATE).get()
182+
assertEquals(Value.DATE, exDate.getParameter<Value>(Parameter.VALUE).get())
183+
assertEquals(
184+
dateValue("20200707"),
185+
main.getProperty<ExDate<*>>(Property.EXDATE)?.getOrNull()?.dates?.first()
186+
)
187+
assertTrue(result.exceptions.isEmpty())
188+
}
189+
151190
@Test
152191
fun `mapToVEvents rewrites cancelled exception using UTC to EXDATE`() {
153192
val result = handler.mapToVEvents(

lib/src/test/kotlin/at/bitfire/synctools/mapping/calendar/handler/RecurrenceFieldHandlerTest.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import at.bitfire.dateValue
1515
import io.mockk.mockk
1616
import junit.framework.TestCase.assertEquals
1717
import net.fortuna.ical4j.model.DateList
18+
import net.fortuna.ical4j.model.Parameter
1819
import net.fortuna.ical4j.model.ParameterList
1920
import net.fortuna.ical4j.model.Property
2021
import net.fortuna.ical4j.model.Recur
@@ -132,10 +133,10 @@ class RecurrenceFieldHandlerTest {
132133
"FREQ=WEEKLY;COUNT=1",
133134
result.getProperties<ExRule<Temporal>>(Property.EXRULE).joinToString { it.value }
134135
)
135-
assertEquals(
136-
"20260201",
137-
result.getProperties<ExDate<Temporal>>(Property.EXDATE).joinToString { it.value }
138-
)
136+
// All-day EXDATE must have VALUE=DATE
137+
val exDates = result.getProperties<ExDate<Temporal>>(Property.EXDATE)
138+
assertEquals(Value.DATE, exDates.first().getParameter<Value>(Parameter.VALUE).get())
139+
assertEquals("20260201", exDates.joinToString { it.value })
139140
}
140141

141142
@Test

0 commit comments

Comments
 (0)