Commit 26776561 authored by Ricki Hirner's avatar Ricki Hirner 🐑

Remove unnecessary X- properties in sent VTIMEZONEs (for libical compatibility)

See https://github.com/libical/libical/pull/446
parent e4c50b34
Pipeline #207131063 passed with stages
in 7 minutes and 32 seconds
......@@ -256,12 +256,8 @@ class Event: ICalendar() {
dtStarts.addAll(exceptions.mapNotNull { it.dtStart?.date })
val earliest = dtStarts.minOrNull()
// add VTIMEZONE components
usedTimeZones.forEach {
var tz = it.vTimeZone
if (earliest != null)
tz = minifyVTimeZone(tz, earliest)
ical.components += tz
}
for (tz in usedTimeZones)
ical.components += minifyVTimeZone(tz.vTimeZone, earliest)
softValidate(ical)
CalendarOutputter(false).output(ical, os)
......
......@@ -16,10 +16,7 @@ import net.fortuna.ical4j.model.Parameter
import net.fortuna.ical4j.model.Property
import net.fortuna.ical4j.model.component.*
import net.fortuna.ical4j.model.parameter.Related
import net.fortuna.ical4j.model.property.ProdId
import net.fortuna.ical4j.model.property.RDate
import net.fortuna.ical4j.model.property.RRule
import net.fortuna.ical4j.model.property.TzUrl
import net.fortuna.ical4j.model.property.*
import net.fortuna.ical4j.validate.ValidationException
import java.io.Reader
import java.io.StringReader
......@@ -119,80 +116,80 @@ open class ICalendar {
* Additionally, TZURL properties are filtered.
*
* @param originalTz time zone definition to minify
* @param start start date for components (usually DTSTART)
* @param start start date for components (usually DTSTART); *null* if unknown
* @return minified time zone definition
*/
fun minifyVTimeZone(originalTz: VTimeZone, start: Date): VTimeZone {
fun minifyVTimeZone(originalTz: VTimeZone, start: Date?): VTimeZone {
val newTz = originalTz.copy() as VTimeZone
val keep = mutableSetOf<Observance>()
// find latest matching STANDARD/DAYLIGHT observances
var latestDaylight: Pair<Date, Observance>? = null
var latestStandard: Pair<Date, Observance>? = null
for (observance in newTz.observances) {
val latest = observance.getLatestOnset(start)
if (start != null) {
// find latest matching STANDARD/DAYLIGHT observances
var latestDaylight: Pair<Date, Observance>? = null
var latestStandard: Pair<Date, Observance>? = null
for (observance in newTz.observances) {
val latest = observance.getLatestOnset(start)
if (latest == null) // observance begins after "start", keep in any case
keep += observance
else
when (observance) {
is Standard ->
if (latestStandard == null || latest > latestStandard.first)
latestStandard = Pair(latest, observance)
is Daylight ->
if (latestDaylight == null || latest > latestDaylight.first)
latestDaylight = Pair(latest, observance)
}
}
if (latest == null) // observance begins after "start", keep in any case
keep += observance
else
when (observance) {
is Standard ->
if (latestStandard == null || latest > latestStandard.first)
latestStandard = Pair(latest, observance)
is Daylight ->
if (latestDaylight == null || latest > latestDaylight.first)
latestDaylight = Pair(latest, observance)
}
}
// keep latest STANDARD observance
latestStandard?.second?.let { keep += it }
// Check latest DAYLIGHT for whether it can apply in the future. Otherwise, DST is not
// used in this time zone anymore and the DAYLIGHT component can be dropped completely.
latestDaylight?.second?.let { daylight ->
// check whether start time is in DST
if (latestStandard != null) {
val latestStandardOnset = latestStandard.second.getLatestOnset(start)
val latestDaylightOnset = daylight.getLatestOnset(start)
if (latestStandardOnset != null && latestDaylightOnset != null && latestDaylightOnset > latestStandardOnset) {
// we're currently in DST
keep += daylight
return@let
// keep latest STANDARD observance
latestStandard?.second?.let { keep += it }
// Check latest DAYLIGHT for whether it can apply in the future. Otherwise, DST is not
// used in this time zone anymore and the DAYLIGHT component can be dropped completely.
latestDaylight?.second?.let { daylight ->
// check whether start time is in DST
if (latestStandard != null) {
val latestStandardOnset = latestStandard.second.getLatestOnset(start)
val latestDaylightOnset = daylight.getLatestOnset(start)
if (latestStandardOnset != null && latestDaylightOnset != null && latestDaylightOnset > latestStandardOnset) {
// we're currently in DST
keep += daylight
return@let
}
}
}
// check RRULEs
for (rRule in daylight.getProperties<RRule>(Property.RRULE)) {
val nextDstOnset = rRule.recur.getNextDate(daylight.startDate.date, start)
if (nextDstOnset != null) {
// there will be a DST onset in the future -> keep DAYLIGHT
keep += daylight
return@let
// check RRULEs
for (rRule in daylight.getProperties<RRule>(Property.RRULE)) {
val nextDstOnset = rRule.recur.getNextDate(daylight.startDate.date, start)
if (nextDstOnset != null) {
// there will be a DST onset in the future -> keep DAYLIGHT
keep += daylight
return@let
}
}
}
// no RRULE, check whether there's an RDATE in the future
for (rDate in daylight.getProperties<RDate>(Property.RDATE)) {
if (rDate.dates.any { it >= start }) {
// RDATE in the future
keep += daylight
return@let
// no RRULE, check whether there's an RDATE in the future
for (rDate in daylight.getProperties<RDate>(Property.RDATE)) {
if (rDate.dates.any { it >= start }) {
// RDATE in the future
keep += daylight
return@let
}
}
}
}
// remove all observances that shall not be kept
val iterator = newTz.observances.iterator() as MutableIterator<Observance>
while (iterator.hasNext()) {
val entry = iterator.next()
if (!keep.contains(entry))
iterator.remove()
// remove all observances that shall not be kept
val iterator = newTz.observances.iterator() as MutableIterator<Observance>
while (iterator.hasNext()) {
val entry = iterator.next()
if (!keep.contains(entry))
iterator.remove()
}
}
// remove TZURL
newTz.properties.filterIsInstance<TzUrl>().forEach {
newTz.properties.remove(it)
}
// remove unnecessary properties
newTz.properties.removeAll { it is TzUrl || it is XProperty }
// validate minified timezone
try {
......
......@@ -238,12 +238,8 @@ class Task: ICalendar() {
completedAt?.date
).filterNotNull().min()
// add VTIMEZONE components
usedTimeZones.forEach {
var tz = it.vTimeZone
if (earliest != null)
tz = minifyVTimeZone(tz, earliest)
ical.components += tz
}
for (tz in usedTimeZones)
ical.components += minifyVTimeZone(tz.vTimeZone, earliest)
softValidate(ical)
CalendarOutputter(false).output(ical, os)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment