@@ -10,6 +10,7 @@ import android.content.Entity
1010import android.provider.CalendarContract.Events
1111import androidx.annotation.VisibleForTesting
1212import at.bitfire.ical4android.util.DateUtils
13+ import at.bitfire.ical4android.util.TimeApiExtensions.toDuration
1314import at.bitfire.ical4android.util.TimeApiExtensions.toLocalDate
1415import at.bitfire.ical4android.util.TimeApiExtensions.toRfc5545Duration
1516import at.bitfire.synctools.icalendar.requireDtStart
@@ -21,6 +22,7 @@ import net.fortuna.ical4j.model.property.Duration
2122import net.fortuna.ical4j.model.property.RDate
2223import net.fortuna.ical4j.model.property.RRule
2324import java.time.Period
25+ import java.time.temporal.TemporalAmount
2426
2527class DurationBuilder : AndroidEntityBuilder {
2628
@@ -52,15 +54,52 @@ class DurationBuilder: AndroidEntityBuilder {
5254 so we wouldn't have to take care of that. However it expects seconds to be in "P<n>S" format,
5355 whereas we provide an RFC 5545-compliant "PT<n>S", which causes the provider to crash:
5456 https://github.com/bitfireAT/synctools/issues/144. So we must convert it ourselves to be on the safe side. */
57+ val alignedDuration = alignWithDtStart(duration.duration, dtStart)
5558
56- // TODO: adapt DURATION according to DATE/DATE-TIME
59+ /* TemporalAmount can have months and years, but the RFC 5545 value must only contain weeks, days and time.
60+ So we have to recalculate the months/years to days according to their position in the calendar.
5761
58- // TemporalAmount can have months and years, but the RFC 5545 must only contain weeks, days and time.
59- // So we have to recalculate the months/years to days according to their position in the calendar.
60- val durationStr = duration.duration .toRfc5545Duration(dtStart.date.toInstant())
62+ The calendar provider accepts every DURATION that `com.android.calendarcommon2.Duration` can parse,
63+ which is weeks, days, hours, minutes and seconds. */
64+ val durationStr = alignedDuration .toRfc5545Duration(dtStart.date.toInstant())
6165 values.put(Events .DURATION , durationStr)
6266 }
6367
68+ /* *
69+ * Aligns the given temporal amount (taken from DURATION) to the VALUE-type (DATE-TIME/DATE) of DTSTART.
70+ *
71+ * @param amount temporal amount that shall be aligned
72+ * @param dtStart DTSTART to compare with
73+ *
74+ * @return Temporal amount that is
75+ *
76+ * - a [Period] (days/months/years that can't be represented by an exact number of seconds) when [dtStart] is a DATE, and
77+ * - a [java.time.Duration] (exact time that can be represented by an exact number of seconds) when [dtStart] is a DATE-TIME.
78+ */
79+ @VisibleForTesting
80+ internal fun alignWithDtStart (amount : TemporalAmount , dtStart : DtStart ): TemporalAmount {
81+ if (DateUtils .isDate(dtStart)) {
82+ // DTSTART is DATE
83+ return if (amount is java.time.Duration ) {
84+ // amount is Duration, change to Period of seconds instead
85+ Period .ofDays(amount.toDays().toInt())
86+ } else {
87+ // amount is already Period
88+ amount
89+ }
90+
91+ } else {
92+ // DTSTART is DATE-TIME
93+ return if (amount is java.time.Period ) {
94+ // amount is Period, change to Duration instead
95+ amount.toDuration(dtStart.date.toInstant())
96+ } else {
97+ // amount is already Duration
98+ amount
99+ }
100+ }
101+ }
102+
64103 @VisibleForTesting
65104 internal fun calculateFromDtEnd (dtStart : DtStart , dtEnd : DtEnd ? ): Duration ? {
66105 if (dtEnd == null )
0 commit comments