[icalendar] Fixed issues with moved events and wrong displayed events (#9678)
* [icalendar] Fixed issues with moved events and wrong displayed events * [icalendar] Changed test to use local time in test-calendar of #9647 Fixes #9647. Handling for RFC 5545's RECURRENCE-ID field inside of events and rounded begin (and based on it end) down in subsecond precision. The calendar does not define a timezone. On a machine that uses a different timezone than Europe/Berlin the test failed. As the behavior itself is specified, the test was changed to use local time for its dates. Signed-off-by: Michael Wodniok <michi@noorganization.org>pull/9685/head
parent
a63acf000e
commit
91dc80449b
|
@ -56,6 +56,7 @@ import org.slf4j.LoggerFactory;
|
|||
* The {@link EventFilterHandler} filters events from a calendar and presents them in a dynamic way.
|
||||
*
|
||||
* @author Michael Wodniok - Initial Contribution
|
||||
* @author Michael Wodniok - Fixed subsecond search if rounding to unit
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EventFilterHandler extends BaseThingHandler implements CalendarUpdateListener {
|
||||
|
@ -331,7 +332,7 @@ public class EventFilterHandler extends BaseThingHandler implements CalendarUpda
|
|||
case HOUR:
|
||||
refDT = refDT.with(ChronoField.MINUTE_OF_HOUR, 0);
|
||||
case MINUTE:
|
||||
refDT = refDT.with(ChronoField.SECOND_OF_MINUTE, 0);
|
||||
refDT = refDT.with(ChronoField.SECOND_OF_MINUTE, 0).with(ChronoField.NANO_OF_SECOND, 0);
|
||||
}
|
||||
reference = refDT.toInstant();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import biweekly.component.VEvent;
|
|||
import biweekly.io.TimezoneAssignment;
|
||||
import biweekly.io.TimezoneInfo;
|
||||
import biweekly.io.text.ICalReader;
|
||||
import biweekly.parameter.Range;
|
||||
import biweekly.property.Comment;
|
||||
import biweekly.property.Contact;
|
||||
import biweekly.property.DateEnd;
|
||||
|
@ -41,10 +42,12 @@ import biweekly.property.DateStart;
|
|||
import biweekly.property.Description;
|
||||
import biweekly.property.DurationProperty;
|
||||
import biweekly.property.Location;
|
||||
import biweekly.property.RecurrenceId;
|
||||
import biweekly.property.Status;
|
||||
import biweekly.property.Summary;
|
||||
import biweekly.property.TextProperty;
|
||||
import biweekly.property.Uid;
|
||||
import biweekly.util.ICalDate;
|
||||
import biweekly.util.com.google.ical.compat.javautil.DateIterator;
|
||||
|
||||
/**
|
||||
|
@ -55,6 +58,7 @@ import biweekly.util.com.google.ical.compat.javautil.DateIterator;
|
|||
* @author Michael Wodniok - Initial contribution
|
||||
* @author Andrew Fiddian-Green - Methods getJustBegunEvents() & getJustEndedEvents()
|
||||
* @author Michael Wodniok - Extension for filtered events
|
||||
* @author Michael Wodniok - Added logic for events moved with "RECURRENCE-ID" (issue 9647)
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class BiweeklyPresentableCalendar extends AbstractPresentableCalendar {
|
||||
|
@ -252,7 +256,7 @@ class BiweeklyPresentableCalendar extends AbstractPresentableCalendar {
|
|||
int foundInSeries = 0;
|
||||
while (positiveBeginDates.hasNext()) {
|
||||
final Instant begInst = positiveBeginDates.next().toInstant();
|
||||
if (begInst.isAfter(frameEnd)) {
|
||||
if (begInst.isAfter(frameEnd) || begInst.equals(frameEnd)) {
|
||||
break;
|
||||
}
|
||||
Duration duration = getEventLength(positiveEvent);
|
||||
|
@ -293,8 +297,15 @@ class BiweeklyPresentableCalendar extends AbstractPresentableCalendar {
|
|||
for (final VEvent currentEvent : usedCalendar.getEvents()) {
|
||||
final Status eventStatus = currentEvent.getStatus();
|
||||
boolean positive = (eventStatus == null || (eventStatus.isTentative() || eventStatus.isConfirmed()));
|
||||
final Collection<VEvent> positiveOrNegativeEvents = (positive ? positiveEvents : negativeEvents);
|
||||
positiveOrNegativeEvents.add(currentEvent);
|
||||
final RecurrenceId eventRecurrenceId = currentEvent.getRecurrenceId();
|
||||
if (positive && eventRecurrenceId != null) {
|
||||
// RecurrenceId moves an event. This blocks other events of series and creates a new single instance
|
||||
positiveEvents.add(currentEvent);
|
||||
negativeEvents.add(currentEvent);
|
||||
} else {
|
||||
final Collection<VEvent> positiveOrNegativeEvents = (positive ? positiveEvents : negativeEvents);
|
||||
positiveOrNegativeEvents.add(currentEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,12 +397,32 @@ class BiweeklyPresentableCalendar extends AbstractPresentableCalendar {
|
|||
for (final VEvent counterEvent : counterEvents) {
|
||||
final Uid counterEventUid = counterEvent.getUid();
|
||||
if (counterEventUid != null && eventUid.getValue().contentEquals(counterEventUid.getValue())) {
|
||||
final DateIterator counterStartDates = getRecurredEventDateIterator(counterEvent);
|
||||
counterStartDates.advanceTo(Date.from(startInstant));
|
||||
if (counterStartDates.hasNext()) {
|
||||
final Instant counterStartInstant = counterStartDates.next().toInstant();
|
||||
if (counterStartInstant.equals(startInstant)) {
|
||||
return true;
|
||||
final RecurrenceId counterRecurrenceId = counterEvent.getRecurrenceId();
|
||||
if (counterRecurrenceId != null) {
|
||||
ICalDate recurrenceDate = counterRecurrenceId.getValue();
|
||||
if (recurrenceDate != null) {
|
||||
Instant recurrenceInstant = Instant.ofEpochMilli(recurrenceDate.getTime());
|
||||
if (recurrenceInstant.equals(startInstant)) {
|
||||
return true;
|
||||
}
|
||||
Range futureOrPast = counterRecurrenceId.getRange();
|
||||
if (futureOrPast != null && futureOrPast.equals(Range.THIS_AND_FUTURE)
|
||||
&& startInstant.isAfter(recurrenceInstant)) {
|
||||
return true;
|
||||
}
|
||||
if (futureOrPast != null && futureOrPast.equals(Range.THIS_AND_PRIOR)
|
||||
&& startInstant.isBefore(recurrenceInstant)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final DateIterator counterStartDates = getRecurredEventDateIterator(counterEvent);
|
||||
counterStartDates.advanceTo(Date.from(startInstant));
|
||||
if (counterStartDates.hasNext()) {
|
||||
final Instant counterStartInstant = counterStartDates.next().toInstant();
|
||||
if (counterStartInstant.equals(startInstant)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -44,12 +46,15 @@ public class BiweeklyPresentableCalendarTest {
|
|||
private AbstractPresentableCalendar calendar;
|
||||
private AbstractPresentableCalendar calendar2;
|
||||
private AbstractPresentableCalendar calendar3;
|
||||
private AbstractPresentableCalendar calendar_issue9647;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws IOException, CalendarException {
|
||||
calendar = new BiweeklyPresentableCalendar(new FileInputStream("src/test/resources/test.ics"));
|
||||
calendar2 = new BiweeklyPresentableCalendar(new FileInputStream("src/test/resources/test2.ics"));
|
||||
calendar3 = new BiweeklyPresentableCalendar(new FileInputStream("src/test/resources/test3.ics"));
|
||||
calendar_issue9647 = new BiweeklyPresentableCalendar(
|
||||
new FileInputStream("src/test/resources/test-issue9647.ics"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -585,5 +590,20 @@ public class BiweeklyPresentableCalendarTest {
|
|||
List<Event> realFilteredEvents6 = calendar.getFilteredEventsBetween(Instant.parse("2019-09-15T06:00:00Z"),
|
||||
Instant.parse("2019-12-31T00:00:00Z"), null, 3);
|
||||
assertEquals(0, realFilteredEvents6.size());
|
||||
|
||||
List<Event> realFilteredEvents7 = calendar_issue9647.getFilteredEventsBetween(
|
||||
LocalDate.parse("2021-01-01").atStartOfDay(ZoneId.systemDefault()).toInstant(),
|
||||
LocalDate.parse("2021-01-02").atStartOfDay(ZoneId.systemDefault()).toInstant(), null, 3);
|
||||
assertEquals(0, realFilteredEvents7.size());
|
||||
|
||||
Event[] expectedFilteredEvents8 = new Event[] {
|
||||
new Event("Restabfall", LocalDate.parse("2021-01-04").atStartOfDay(ZoneId.systemDefault()).toInstant(),
|
||||
LocalDate.parse("2021-01-05").atStartOfDay(ZoneId.systemDefault()).toInstant(), ""),
|
||||
new Event("Gelbe Tonne", LocalDate.parse("2021-01-04").atStartOfDay(ZoneId.systemDefault()).toInstant(),
|
||||
LocalDate.parse("2021-01-05").atStartOfDay(ZoneId.systemDefault()).toInstant(), "") };
|
||||
List<Event> realFilteredEvents8 = calendar_issue9647.getFilteredEventsBetween(
|
||||
LocalDate.parse("2021-01-04").atStartOfDay(ZoneId.systemDefault()).toInstant(),
|
||||
LocalDate.parse("2021-01-05").atStartOfDay(ZoneId.systemDefault()).toInstant(), null, 3);
|
||||
assertArrayEquals(expectedFilteredEvents8, realFilteredEvents8.toArray(new Event[] {}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
BEGIN:VCALENDAR
|
||||
PRODID:-//Google Inc//Google Calendar 70.9054//EN
|
||||
VERSION:2.0
|
||||
CALSCALE:GREGORIAN
|
||||
METHOD:PUBLISH
|
||||
X-WR-CALNAME:Entsorgung
|
||||
X-WR-TIMEZONE:Europe/Amsterdam
|
||||
X-WR-CALDESC:Entleerungstermine Mülltonnen
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20210104
|
||||
DTEND;VALUE=DATE:20210105
|
||||
DTSTAMP:20210102T150339Z
|
||||
UID:pseudo7346893o7r8328zheh@google.com
|
||||
RECURRENCE-ID;VALUE=DATE:20210101
|
||||
CREATED:20200708T110847Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20210102T135837Z
|
||||
LOCATION:
|
||||
SEQUENCE:8
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:Restabfall
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20210104
|
||||
DTEND;VALUE=DATE:20210105
|
||||
DTSTAMP:20210102T150339Z
|
||||
UID:pseudo352763jug7bhd7528237t@google.com
|
||||
RECURRENCE-ID;VALUE=DATE:20210101
|
||||
CREATED:20200708T110659Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20210102T135834Z
|
||||
LOCATION:
|
||||
SEQUENCE:8
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:Gelbe Tonne
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20201226
|
||||
DTEND;VALUE=DATE:20201227
|
||||
DTSTAMP:20210102T150339Z
|
||||
UID:pseudo32zhibh75fvdv763b713@google.com
|
||||
RECURRENCE-ID;VALUE=DATE:20201225
|
||||
CREATED:20200708T110333Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20200708T111046Z
|
||||
LOCATION:
|
||||
SEQUENCE:1
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:Blaue Tonne
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20200717
|
||||
DTEND;VALUE=DATE:20200718
|
||||
RRULE:FREQ=WEEKLY;WKST=MO;INTERVAL=2;BYDAY=FR
|
||||
DTSTAMP:20210102T150339Z
|
||||
UID:pseudo7346893o7r8328zheh@google.com
|
||||
CREATED:20200708T110847Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20200708T110851Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:Restabfall
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20200714
|
||||
DTEND;VALUE=DATE:20200715
|
||||
RRULE:FREQ=WEEKLY;WKST=MO;BYDAY=TU
|
||||
DTSTAMP:20210102T150339Z
|
||||
UID:pseudolo2p0394z2edxc1223@google.com
|
||||
CREATED:20200708T110802Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20200708T110820Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:Bio-Tonne
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20200717
|
||||
DTEND;VALUE=DATE:20200718
|
||||
RRULE:FREQ=WEEKLY;WKST=MO;INTERVAL=2;BYDAY=FR
|
||||
DTSTAMP:20210102T150339Z
|
||||
UID:pseudo352763jug7bhd7528237t@google.com
|
||||
CREATED:20200708T110659Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20200708T110729Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:Gelbe Tonne
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20200710
|
||||
DTEND;VALUE=DATE:20200711
|
||||
RRULE:FREQ=WEEKLY;WKST=MO;INTERVAL=2;BYDAY=FR
|
||||
DTSTAMP:20210102T150339Z
|
||||
UID:pseudo32zhibh75fvdv763b713@google.com
|
||||
CREATED:20200708T110333Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20200708T110720Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:Blaue Tonne
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
Loading…
Reference in New Issue