update icalparser to 1e68aae75dbac8f7c27df7b047c0404154d436c4 (supports php 8.2)

This commit is contained in:
root 2023-07-16 02:51:26 +02:00
parent d48cf5c579
commit 05f1245d30
27 changed files with 1572 additions and 586 deletions

View file

@ -0,0 +1,21 @@
<?php
namespace tests;
/**
* Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
*
* @license BSD-3-Clause
* @author Roman Ožana <roman@ozana.cz>
*/
require_once __DIR__ . '/../vendor/autoload.php';
use Closure;
use Tester\Environment;
function test($description, Closure $fn): void {
printf("• %s%s%s", $description, PHP_EOL, $fn());
}
Environment::setup();

View file

@ -0,0 +1,59 @@
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:Office Opening Hours
X-WR-TIMEZONE:Europe/London
BEGIN:VTIMEZONE
TZID:Europe/London
X-LIC-LOCATION:Europe/London
BEGIN:DAYLIGHT
TZOFFSETFROM:+0000
TZOFFSETTO:+0100
TZNAME:BST
DTSTART:19700329T010000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0100
TZOFFSETTO:+0000
TZNAME:GMT
DTSTART:19701025T020000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=Europe/London:20190401T090000
DTEND;TZID=Europe/London:20190401T170000
RRULE:FREQ=WEEKLY;WKST=SU;BYDAY=MO,TU,WE,TH,FR
DTSTAMP:20190402T174536Z
UID:1nibcosj8r05bjoia671im7ulg@google.com
CREATED:20190401T144832Z
DESCRIPTION:
LAST-MODIFIED:20190401T145024Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Office Opening Hours
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=Europe/London:20190225T090000
DTEND;TZID=Europe/London:20190225T170000
RRULE:FREQ=WEEKLY;WKST=SU;UNTIL=20190329T235959Z;BYDAY=MO,TU,WE,TH,FR
DTSTAMP:20190402T174536Z
UID:7e581hcu1ub3nm0bb6c4o29suj@google.com
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Office
Opening Hours;X-NUM-GUESTS=0:mailto:poweredpasture.com_la2jmsbphe5h11351kk
scnnqtg@group.calendar.google.com
CREATED:20190227T164630Z
DESCRIPTION:
LAST-MODIFIED:20190401T144725Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Office Opening Hours
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR

View file

@ -0,0 +1,47 @@
BEGIN:VCALENDAR
PRODID;X-RICAL-TZSOURCE=TZINFO:-//Airbnb Inc//Hosting Calendar 0.8.8//EN
CALSCALE:GREGORIAN
VERSION:2.0
BEGIN:VEVENT
DTEND;VALUE=DATE:20220520
DTSTART;VALUE=DATE:20220412
UID:1418fdfasfdasdfsad@airbnb.com
DESCRIPTION:Reservation URL: https://www.airbnb.com/hosting/reservations/
details/HMQHSAR9SE\nPhone Number (Last 4 Digits): 0431
SUMMARY:Reserved
END:VEVENT
BEGIN:VEVENT
DTEND;VALUE=DATE:20220620
DTSTART;VALUE=DATE:20220617
UID:1418fb94e984-dfasdfasdfsdfsdfsd@airbnb.com
DESCRIPTION:Reservation URL: https://www.airbnb.com/hosting/reservations/
details/HMWQXYQSM4\nPhone Number (Last 4 Digits): 2360
SUMMARY:Reserved
END:VEVENT
BEGIN:VEVENT
DTEND;VALUE=DATE:20220628
DTSTART;VALUE=DATE:20220625
UID:1418fb94e984-dafdfdfadfdfadsfasdafsd@airbnb.com
DESCRIPTION:Reservation URL: https://www.airbnb.com/hosting/reservations/
details/HM49HZXKQT\nPhone Number (Last 4 Digits): 1537
SUMMARY:Reserved
END:VEVENT
BEGIN:VEVENT
DTEND;VALUE=DATE:20220724
DTSTART;VALUE=DATE:20220723
UID:6fec1092d3fa-afdfasdfdsfasdfasdfsdfasd@airbnb.com
SUMMARY:Airbnb (Not available)
END:VEVENT
BEGIN:VEVENT
DTEND;VALUE=DATE:20220807
DTSTART;VALUE=DATE:20220725
UID:6fec1092d3fa-afdfsdfsdfasdfadsfsfs@airbnb.com
SUMMARY:Airbnb (Not available)
END:VEVENT
BEGIN:VEVENT
DTEND;VALUE=DATE:20230512
DTSTART;VALUE=DATE:20221107
UID:6fec1092d3fa-afdafdsafsdfdfsdfsd@airbnb.com
SUMMARY:Airbnb (Not available)
END:VEVENT
END:VCALENDAR

View file

@ -0,0 +1,39 @@
BEGIN:VCALENDAR
VERSION:2.0
X-WR-CALNAME:URL
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:Europe/Berlin
TZURL:http://tzurl.org/zoneinfo-outlook/Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:111
DTSTAMP:20181123T192651Z
CATEGORIES;LANGUAGE=de-DE:Party
CONTACT:
DESCRIPTION:xxx
DTSTART;TZID=Europe/Berlin:20160415T210000
DTEND;TZID=Europe/Berlin:20160416T040000
LOCATION:xxx
RDATE;TZID=Europe/Berlin:20161216T210000
RDATE;TZID=Europe/Berlin:20161223T210000
RDATE;TZID=Europe/Berlin:20161230T210000
SEQUENCE:0
SUMMARY:xxx
END:VEVENT
END:VCALENDAR

View file

@ -0,0 +1,67 @@
BEGIN:VCALENDAR
VERSION:2.0
PRODID:Zimbra-Calendar-Provider
BEGIN:VTIMEZONE
TZID:America/Los_Angeles
BEGIN:STANDARD
DTSTART:19710101T020000
TZOFFSETTO:-0800
TZOFFSETFROM:-0700
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU
TZNAME:PST
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19710101T020000
TZOFFSETTO:-0700
TZOFFSETFROM:-0800
RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU
TZNAME:PDT
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:1334F9B7-6136-444E-A58D-472564C6AA73
SUMMARY:sahaja <> frashed
DESCRIPTION:weekly 1on1
CATEGORIES:one, two, three
ATTENDEE;CN=James Lal;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS
-ACTION;RSVP=TRUE:mailto:jlal@mozilla.com
ORGANIZER;CN=Faramarz Rashed:mailto:frashed@mozilla.com
DTSTART;TZID=America/Los_Angeles:20120326T110000
DTEND;TZID=America/Los_Angeles:20120326T113000
STATUS:CONFIRMED
CLASS:PUBLIC
TRANSP:OPAQUE
LAST-MODIFIED:20120326T161522Z
DTSTAMP:20120730T165637Z
SEQUENCE:9
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER;RELATED=START:-PT5M
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
BEGIN:VEVENT
UID:14556F9B7-6136-444E-A58D-472564C6AA73
SUMMARY:something something
DESCRIPTION:weekly 1on1
CATEGORIES:one
CATEGORIES:two
CATEGORIES:three
ATTENDEE;CN=James Lal;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS
-ACTION;RSVP=TRUE:mailto:jlal@mozilla.com
ORGANIZER;CN=Faramarz Rashed:mailto:frashed@mozilla.com
DTSTART;TZID=America/Los_Angeles:20120326T110000
DTEND;TZID=America/Los_Angeles:20120326T113000
STATUS:CONFIRMED
CLASS:PUBLIC
TRANSP:OPAQUE
LAST-MODIFIED:20120326T161522Z
DTSTAMP:20120730T165637Z
SEQUENCE:9
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER;RELATED=START:-PT5M
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR

View file

@ -0,0 +1,34 @@
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
X-WR-CALNAME:calmozilla1@gmail.com
X-WR-TIMEZONE:America/Los_Angeles
BEGIN:VTIMEZONE
TZID:America/Los_Angeles
X-LIC-LOCATION:America/Los_Angeles
BEGIN:DAYLIGHT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
TZNAME:PDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
TZNAME:PST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=America/Los_Angeles:20230131T050000
DTEND;TZID=America/Los_Angeles:20230131T060000
RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20230228T090000;INTERVAL=2;BYDAY=TU
DTSTAMP:20120803T221236Z
DESCRIPTION:
SUMMARY:Every day recurring
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR

View file

@ -0,0 +1,32 @@
BEGIN:VCALENDAR
VERSION:2.0
X-WR-CALNAME:URL
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:Europe/Berlin
TZURL:http://tzurl.org/zoneinfo-outlook/Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTAMP:20191106T093607Z
UID:20191106T093607Z-791992399@marudot.com
DTSTART;VALUE=DATE:20191101
DTEND;VALUE=DATE:20191102
SUMMARY:Example event
URL:https%3A%2F%2Fgithub.com%2FOzzyCzech%2Ficalparser%2F
END:VEVENT
END:VCALENDAR

View file

@ -0,0 +1,26 @@
<?php
/**
* @author PC Drew <pc@soprisapps.com>
*/
use om\IcalParser;
use Tester\Assert;
use Tester\Environment;
use function tests\test;
require_once __DIR__ . '/bootstrap.php';
date_default_timezone_set('Europe/Prague');
test('Event with multiple ATTACHMENTS', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/multiple_attachments.ics');
$first = $cal->getEvents()->getIterator()->current();
// Backwards compatibility, there is only ever one key displayed
Assert::hasKey('ATTACH', $first);
Assert::type('string', $first['ATTACH']);
// The new key 'ATTACHMENTS' is an array with 1 or more attachments
Assert::type('array', $first['ATTACHMENTS']);
Assert::count(2, $first['ATTACHMENTS']);
});

View file

@ -0,0 +1,25 @@
<?php
/**
* Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
*
* @license BSD-3-Clause
* @author Roman Ožana <roman@ozana.cz>
*/
use om\IcalParser;
use Tester\Assert;
use function tests\test;
require_once __DIR__ . '/bootstrap.php';
date_default_timezone_set('Europe/Prague');
test('Multiple categories test', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/multiple_categories.ics');
$events = $cal->getEvents()->sorted();
foreach ($events as $event) {
Assert::type('array', $event['CATEGORIES']);
Assert::same(['one', 'two', 'three'], $event['CATEGORIES']);
}
});

View file

@ -0,0 +1,25 @@
<?php
/**
* Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
*
* @license BSD-3-Clause
* @author Roman Ožana <roman@ozana.cz>
*/
use om\IcalParser;
use Tester\Assert;
use function tests\test;
require_once __DIR__ . '/bootstrap.php';
test('Events with wrong dates', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/wrong_dates.ics');
$events = $cal->getEvents()->sorted();
Assert::same('29.9.2014 00:00:00', $events[1]['DTSTART']->format('j.n.Y H:i:s'));
Assert::same(null, $events[1]['DTEND']);
Assert::same(null, $events[0]['DTSTART']);
Assert::same('30.9.2014 00:00:00', $events[0]['DTEND']->format('j.n.Y H:i:s'));
});

View file

@ -0,0 +1,34 @@
<?php
/**
* Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
*
* @license BSD-3-Clause
* @author Roman Ožana <roman@ozana.cz>
*/
use om\IcalParser;
use Tester\Assert;
use function tests\test;
require_once __DIR__ . '/bootstrap.php';
date_default_timezone_set('Europe/Prague');
test('Blank description test', function () {
$cal = new IcalParser();
$results = $cal->parseFile(__DIR__ . '/cal/blank_description.ics');
$first = $cal->getEvents()->getIterator()->current();
Assert::hasKey('DESCRIPTION', $first);
Assert::same('', $first['DESCRIPTION']);
});
test('Multiple lines description', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/multiline_description.ics');
$events = $cal->getEvents()->sorted();
$first = $events->getIterator()->current();
Assert::same('30.6.2012 06:00:00', $first['DTSTART']->format('j.n.Y H:i:s'));
Assert::same("Here is a description that spans multiple lines!\n\nThis should be on a new line as well because the description contains newline characters.", $first['DESCRIPTION']);
});

View file

@ -0,0 +1,39 @@
<?php
/**
* @author Marc Vachette <marc.vachette@gmail.com>
*/
use om\IcalParser;
use Tester\Assert;
use function tests\test;
require_once __DIR__ . '/bootstrap.php';
date_default_timezone_set('Europe/Paris');
test('Normal time zone', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/blank_description.ics');
Assert::same('America/Los_Angeles', $cal->timezone->getName());
});
test('Negative zero UTC timezone', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/utc_negative_zero.ics');
Assert::same('Etc/GMT', $cal->timezone->getName());
});
/**
* Time zone with custom prefixes (Mozilla files tken from here: https://www.mozilla.org/en-US/projects/calendar/holidays/)
*/
test('Time zone with custom prefixes', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/FrenchHolidays.ics');
Assert::same('Europe/Paris', $cal->timezone->getName());
});
test('Weird windows timezones', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/weird_windows_timezones.ics');
$cal->getEvents()->sorted();
Assert::same('Atlantic/Reykjavik', $cal->timezone->getName());
});

View file

@ -0,0 +1,22 @@
<?php
/**
* Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
*
* @license BSD-3-Clause
* @author Roman Ožana <roman@ozana.cz>
*/
use om\IcalParser;
use Tester\Assert;
use function tests\test;
require_once __DIR__ . '/bootstrap.php';
test('URL parsing check', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/url.ics');
$first = $cal->getEvents()->getIterator()->current();
Assert::hasKey('URL', $first);
Assert::same($first['URL'], urlencode('https://github.com/OzzyCzech/icalparser/'));
});

View file

@ -0,0 +1,260 @@
<?php
/**
* @author PC Drew <pc@schoolblocks.com>
* @author Roman Ožana <roman@ozana.cz>
*/
use om\IcalParser;
use Tester\Assert;
use function tests\test;
require_once __DIR__ . '/bootstrap.php';
test('Recurring instances finite', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/recur_instances_finite.ics');
$events = $cal->getEvents()->sorted();
// DTSTART;TZID=America/Los_Angeles:20121002T100000
// DTEND;TZID=America/Los_Angeles:20121002T103000
// RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=1TU;UNTIL=20121231T100000
// RDATE;TZID=America/Los_Angeles:20121110T100000
// RDATE;TZID=America/Los_Angeles:20121105T100000
Assert::equal(5, $events->count());
Assert::equal('2.10.2012 10:00:00', $events[0]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('5.11.2012 10:00:00', $events[1]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('6.11.2012 10:00:00', $events[2]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('10.11.2012 10:00:00', $events[3]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('4.12.2012 10:00:00', $events[4]['DTSTART']->format('j.n.Y H:i:s'));
});
test('Recurring instance check', function () {
$cal = new IcalParser();
$results = $cal->parseFile(__DIR__ . '/cal/recur_instances.ics');
$events = $cal->getEvents()->sorted();
$recurrences = [];
foreach ($events as $i => $event) {
$recurrences[] = $event['DTSTART'];
}
// DTSTART;TZID=America/Los_Angeles:20121002T100000
// DTEND;TZID=America/Los_Angeles:20121002T103000
// RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=1TU
// RDATE;TZID=America/Los_Angeles:20121105T100000
// RDATE;TZID=America/Los_Angeles:20121110T100000,20121130T100000
// EXDATE;TZID=America/Los_Angeles:20130402T100000
// EXDATE;TZID=America/Los_Angeles:20121204T100000
// EXDATE;TZID=America/Los_Angeles:20130205T100000
// because there is no "UNTIL", we calculate until 3 years from now of repeating events
$now = new DateTime('now');
$diff = $now->diff(new DateTime('20121002T100000'));
$count = ($diff->y + 3) * 12 + $diff->m;
Assert::equal($count, count($recurrences));
Assert::equal('02.10.2012 15:00:00', $recurrences[0]->format('d.m.Y H:i:s'));
Assert::equal('06.11.2012 20:00:00', $recurrences[1]->format('d.m.Y H:i:s'));
Assert::equal('10.11.2012 10:00:00', $recurrences[2]->format('d.m.Y H:i:s'));
Assert::equal('30.11.2012 10:00:00', $recurrences[3]->format('d.m.Y H:i:s'));
Assert::equal('01.01.2013 10:00:00', $recurrences[4]->format('d.m.Y H:i:s'));
Assert::equal('05.03.2013 10:00:00', $recurrences[5]->format('d.m.Y H:i:s'));
Assert::equal('07.05.2013 10:00:00', $recurrences[6]->format('d.m.Y H:i:s'));
Assert::equal('04.06.2013 10:00:00', $recurrences[7]->format('d.m.Y H:i:s'));
Assert::equal('02.07.2013 10:00:00', $recurrences[8]->format('d.m.Y H:i:s'));
Assert::equal('06.08.2013 10:00:00', $recurrences[9]->format('d.m.Y H:i:s'));
Assert::equal('03.09.2013 10:00:00', $recurrences[10]->format('d.m.Y H:i:s'));
Assert::equal('01.10.2013 10:00:00', $recurrences[11]->format('d.m.Y H:i:s'));
Assert::equal('05.11.2013 10:00:00', $recurrences[12]->format('d.m.Y H:i:s'));
Assert::equal('03.12.2013 10:00:00', $recurrences[13]->format('d.m.Y H:i:s'));
Assert::equal('07.01.2014 10:00:00', $recurrences[14]->format('d.m.Y H:i:s'));
Assert::equal('04.02.2014 10:00:00', $recurrences[15]->format('d.m.Y H:i:s'));
Assert::equal('04.03.2014 10:00:00', $recurrences[16]->format('d.m.Y H:i:s'));
Assert::equal('01.04.2014 10:00:00', $recurrences[17]->format('d.m.Y H:i:s'));
Assert::equal('06.05.2014 10:00:00', $recurrences[18]->format('d.m.Y H:i:s'));
Assert::equal('03.06.2014 10:00:00', $recurrences[19]->format('d.m.Y H:i:s'));
Assert::equal('01.07.2014 10:00:00', $recurrences[20]->format('d.m.Y H:i:s'));
Assert::equal('05.08.2014 10:00:00', $recurrences[21]->format('d.m.Y H:i:s'));
Assert::equal('02.09.2014 10:00:00', $recurrences[22]->format('d.m.Y H:i:s'));
Assert::equal('07.10.2014 10:00:00', $recurrences[23]->format('d.m.Y H:i:s'));
Assert::equal('04.11.2014 10:00:00', $recurrences[24]->format('d.m.Y H:i:s'));
Assert::equal('02.12.2014 10:00:00', $recurrences[25]->format('d.m.Y H:i:s'));
Assert::equal('06.01.2015 10:00:00', $recurrences[26]->format('d.m.Y H:i:s'));
Assert::equal('03.02.2015 10:00:00', $recurrences[27]->format('d.m.Y H:i:s'));
Assert::equal('03.03.2015 10:00:00', $recurrences[28]->format('d.m.Y H:i:s'));
Assert::equal('07.04.2015 10:00:00', $recurrences[29]->format('d.m.Y H:i:s'));
Assert::equal('05.05.2015 10:00:00', $recurrences[30]->format('d.m.Y H:i:s'));
Assert::equal('02.06.2015 10:00:00', $recurrences[31]->format('d.m.Y H:i:s'));
Assert::equal('07.07.2015 10:00:00', $recurrences[32]->format('d.m.Y H:i:s'));
Assert::equal('04.08.2015 10:00:00', $recurrences[33]->format('d.m.Y H:i:s'));
Assert::equal('01.09.2015 10:00:00', $recurrences[34]->format('d.m.Y H:i:s'));
foreach ($events->getIterator()->current()['EXDATES'] as $exDate) {
Assert::notContains($exDate, $recurrences);
}
});
test('Recurrent event with modifications at single date', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/recur_instances_with_modifications.ics');
$events = $cal->getEvents()->sorted();
// There should be 36 total events because of the modified event + 35 recurrences
Assert::count(36, $events); // 36 events
// There should be 35 total recurrences because the modified event should've removed 1 recurrence
Assert::hasKey('RECURRENCES', $events->offsetGet(1));
$recurrences = $events->getIterator()->current()['RECURRENCES'];
Assert::count(35, $recurrences);
// reccurent event don't have RECURRENCES
foreach (range(2, 35) as $index) {
Assert::hasNotKey('RECURRENCES', $events->offsetGet($index));
}
// the date 8.8.2016 should be modified
$modifiedEvent = $events->offsetGet(0);
Assert::hasNotKey('RECURRENCES', $modifiedEvent);
// the 12th entry is the modified event, related to the remaining recurring events
Assert::same('8.8.2016', $modifiedEvent['DTSTART']->format('j.n.Y'));
Assert::notContains($modifiedEvent['DTSTART'], $recurrences);
});
test('Recuring instances with modifications and interval', function () {
$cal = new IcalParser();
$results = $cal->parseFile(__DIR__ . '/cal/recur_instances_with_modifications_and_interval.ics');
// Build the cache of RECURRENCE-IDs and EXDATES first, so that we can properly determine the interval
$eventCache = [];
foreach ($results['VEVENT'] as $event) {
$eventSequence = empty($event['SEQUENCE']) ? "0" : $event['SEQUENCE'];
$eventRecurrenceID = empty($event['RECURRENCE-ID']) ? "0" : $event['RECURRENCE-ID'];
$eventCache[$event['UID']][$eventRecurrenceID][$eventSequence] = $event;
}
$trueEvents = [];
foreach ($results['VEVENT'] as $event) {
if (empty($event['RECURRENCES'])) {
$trueEvents[] = $event;
} else {
$eventUID = $event['UID'];
foreach ($event['RECURRENCES'] as $recurrence) {
$eventRecurrenceID = $recurrence->format("Ymd");
if (empty($eventCache[$eventUID][$eventRecurrenceID])) {
$trueEvents[$eventRecurrenceID] = ['DTSTART' => $recurrence];
} else {
krsort($eventCache[$eventUID][$eventRecurrenceID]);
$keys = array_keys($eventCache[$eventUID][$eventRecurrenceID]);
$trueEvents[$eventRecurrenceID] = $eventCache[$eventUID][$eventRecurrenceID][$keys[0]];
}
}
}
}
usort(
$trueEvents,
static function ($a, $b): int {
return ($a['DTSTART'] > $b['DTSTART']) ? 1 : -1;
}
);
$events = $cal->getEvents()->sorted()->getArrayCopy();
Assert::false(empty($events[0]['RECURRENCES']));
Assert::equal(count($trueEvents), count($events));
foreach ($trueEvents as $index => $trueEvent) {
Assert::equal($trueEvent['DTSTART']->format("Ymd"), $events[$index]['DTSTART']->format("Ymd"));
}
});
test('', function () {
$cal = new IcalParser();
// There is still an issue that needs to be resolved when modifications are made to the initial event that is the
// base of the recurrences. The below ICS file has a great edge case example: one event, no recurrences in the
// recurring ruleset, and a modification to the initial event.
$results = $cal->parseFile(__DIR__ . '/cal/recur_instances_with_modifications_to_first_day.ics');
$events = $cal->getEvents()->sorted()->getArrayCopy();
Assert::true(empty($events[0]['RECURRENCES'])); // edited event
Assert::true(empty($events[1]['RECURRENCES'])); // recurring event base with no recurrences
Assert::equal(1, count($events));
});
test('', function () {
$cal = new IcalParser();
$results = $cal->parseFile(__DIR__ . '/cal/daily_recur.ics');
$events = $cal->getEvents()->sorted()->getArrayCopy();
$period = new DatePeriod(new DateTime('20120801T050000'), new DateInterval('P1D'), new DateTime('20150801T050000'));
foreach ($period as $i => $day) {
Assert::equal($day->format('j.n.Y H:i:s'), $events[$i]['DTSTART']->format('j.n.Y H:i:s'));
}
});
test('', function () {
$cal = new IcalParser();
$results = $cal->parseFile(__DIR__ . '/cal/daily_recur2.ics');
$events = $cal->getEvents()->sorted()->getArrayCopy();
Assert::equal(4, count($events));
Assert::equal('21.8.2017 00:00:00', $events[0]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('28.8.2017 00:00:00', $events[1]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('4.9.2017 00:00:00', $events[2]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('11.9.2017 00:00:00', $events[3]['DTSTART']->format('j.n.Y H:i:s'));
});
test('', function () {
//https://github.com/OzzyCzech/icalparser/issues/38
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/38_weekly_recurring_event_missing_day.ics');
$events = $cal->getEvents()->sorted()->getArrayCopy();
//first monday
Assert::equal('25.2.2019 09:00:00', $events[0]['DTSTART']->format('j.n.Y H:i:s'));
//rest of week
Assert::equal('26.2.2019 09:00:00', $events[1]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('27.2.2019 09:00:00', $events[2]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('28.2.2019 09:00:00', $events[3]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('1.3.2019 09:00:00', $events[4]['DTSTART']->format('j.n.Y H:i:s'));
//now check the next 4 mondays to make sure they exist as well
Assert::equal('4.3.2019 09:00:00', $events[5]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('11.3.2019 09:00:00', $events[10]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('18.3.2019 09:00:00', $events[15]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('25.3.2019 09:00:00', $events[20]['DTSTART']->format('j.n.Y H:i:s'));
//Last week that works correctly
Assert::equal('1.4.2019 09:00:00', $events[25]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('2.4.2019 09:00:00', $events[26]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('3.4.2019 09:00:00', $events[27]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('4.4.2019 09:00:00', $events[28]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('5.4.2019 09:00:00', $events[29]['DTSTART']->format('j.n.Y H:i:s'));
//This week starts failing
Assert::equal('8.4.2019 09:00:00', $events[30]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('9.4.2019 09:00:00', $events[31]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('10.4.2019 09:00:00', $events[32]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('11.4.2019 09:00:00', $events[33]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('12.4.2019 09:00:00', $events[34]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('15.4.2019 09:00:00', $events[35]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('16.4.2019 09:00:00', $events[36]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('17.4.2019 09:00:00', $events[37]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('18.4.2019 09:00:00', $events[38]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('19.4.2019 09:00:00', $events[39]['DTSTART']->format('j.n.Y H:i:s'));
});
test('Recurring instances bi-weekly', function () {
// https://github.com/OzzyCzech/icalparser/issues/61
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/rrule_interval.ics');
$events = $cal->getEvents()->sorted();
var_dump($events[0]['RECURRENCES']);
// DTSTART;TZID=America/Los_Angeles:20230131T050000
// DTEND;TZID=America/Los_Angeles:20230131T060000
// RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20230228T090000;INTERVAL=2;BYDAY=TU
Assert::equal(3, count($events[0]['RECURRENCES']));
Assert::equal(3, $events->count());
Assert::equal('31.1.2023 05:00:00', $events[0]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('14.2.2023 05:00:00', $events[1]['DTSTART']->format('j.n.Y H:i:s'));
Assert::equal('28.2.2023 05:00:00', $events[2]['DTSTART']->format('j.n.Y H:i:s'));
});

View file

@ -0,0 +1,36 @@
<?php
/**
* Copyright (c) 2004-2022 Roman Ožana (https://ozana.cz)
*
* @license BSD-3-Clause
* @author Roman Ožana <roman@ozana.cz>
*/
use om\IcalParser;
use Tester\Assert;
use function tests\test;
require_once __DIR__ . '/bootstrap.php';
date_default_timezone_set('Europe/Prague');
test('Natural sort order by date', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/basic.ics');
$first = $cal->getEvents()->sorted()->getIterator()->current();
Assert::same('1.1.2013 00:00:00', $first['DTSTART']->format('j.n.Y H:i:s'));
});
test('Reverse events sort (parseFile)', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/basic.ics');
$first = $cal->getEvents()->reversed()->getIterator()->current();
Assert::same('26.12.2015 00:00:00', $first['DTSTART']->format('j.n.Y H:i:s'));
});
test('Reverse events sort (parseString)', function () {
$cal = new IcalParser();
$cal->parseString(file_get_contents(__DIR__ . '/cal/basic.ics'));
$first = $cal->getEvents()->reversed()->getIterator()->current();
Assert::same('26.12.2015 00:00:00', $first['DTSTART']->format('j.n.Y H:i:s'));
});

View file

@ -0,0 +1,24 @@
<?php
use om\IcalParser;
use Tester\Assert;
use function tests\test;
require_once __DIR__ . '/bootstrap.php';
date_default_timezone_set('Europe/Prague');
test('Time zone should remain empty', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/missing-timezone.ics');
Assert::null($cal->timezone);
});
test('Timezone should be same as current timezone', function () {
$cal = new IcalParser();
$cal->parseFile(__DIR__ . '/cal/missing-timezone.ics');
$dtstart = $cal->getEvents()->reversed()->getIterator()->current()['DTSTART'];
/** @var DateTime $dtstart */
Assert::same('Europe/Prague', $dtstart->getTimezone()->getName());
Assert::same('7.11.2022', $dtstart->format('j.n.Y'));
});