Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5dcc112 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

Minify VTIMEZONEs

parent 3a4e2163
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -221,8 +221,12 @@ class Event: ICalendar() {
            exception.dtEnd?.timeZone?.let(usedTimeZones::add)
        }

        // add VTIMEZONE components
        usedTimeZones.forEach { ical.components += it.vTimeZone }
        // add minified VTIMEZONE components
        usedTimeZones.forEach {
            val tz = it.vTimeZone
            dtStart?.let { minifyVTimeZone(tz, it.date) }
            ical.components += tz
        }

        CalendarOutputter(false).output(ical, os)
    }
+57 −2
Original line number Diff line number Diff line
@@ -12,10 +12,11 @@ import net.fortuna.ical4j.data.CalendarBuilder
import net.fortuna.ical4j.data.CalendarParserFactory
import net.fortuna.ical4j.data.ParserException
import net.fortuna.ical4j.model.*
import net.fortuna.ical4j.model.component.VAlarm
import net.fortuna.ical4j.model.component.VTimeZone
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.component.*
import net.fortuna.ical4j.model.property.DateProperty
import net.fortuna.ical4j.model.property.ProdId
import net.fortuna.ical4j.model.property.TzUrl
import net.fortuna.ical4j.util.Strings
import java.io.StringReader
import java.util.*
@@ -60,6 +61,60 @@ open class ICalendar {

        fun isDateTime(date: DateProperty?) = date != null && date.date is DateTime

        /**
         * Minifies a VTIMEZONE so that only components after [start] are kept.
         * Doesn't return the smallest possible VTIMEZONE at the moment, but
         * reduces its size significantly.
         *
         * @param tz      Time zone definition to minify. Attention: the observances of this object
         *                will be modified!
         * @param start   Start date for components
         */
        fun minifyVTimeZone(tz: VTimeZone, start: Date) {
            // find latest matching STANDARD/DAYLIGHT component,
            // keep components at/after "start"
            val iter = tz.observances.iterator()
            var latestDaylight: Pair<Date, Observance>? = null
            var latestStandard: Pair<Date, Observance>? = null
            val keep = mutableSetOf<Observance>()
            while (iter.hasNext()) {
                val entry = iter.next() as Observance
                val latest = entry.getLatestOnset(start)

                if (latest == null  /* observance begins after "start" */ ||
                    latest >= start /* observance has onsets at/after "start" */ ) {
                    keep += entry
                    continue
                }

                when (entry) {
                    is Standard -> {
                        if (latestStandard == null || latest.after(latestStandard.first))
                            latestStandard = Pair(latest, entry)
                    }
                    is Daylight -> {
                        if (latestDaylight == null || latest.after(latestDaylight.first))
                            latestDaylight = Pair(latest, entry)
                    }
                }
            }
            latestStandard?.second?.let { keep += it }
            latestDaylight?.second?.let { keep += it }

            // actually remove all observances that shall not be kept
            val iter2 = tz.observances.iterator()
            while (iter2.hasNext()) {
                val entry = iter2.next() as Observance
                if (!keep.contains(entry))
                    iter2.remove()
            }

            // remove TZURL
            tz.properties.filter { it is TzUrl }.forEach {
                tz.properties.remove(it)
            }
        }

        /**
         * Takes a string with a timezone definition and returns the time zone ID.
         * @param timezoneDef time zone definition (VCALENDAR with VTIMEZONE component)