From e2acc2bd2318e179bca6b2bfc53ad8d6e503ab6d Mon Sep 17 00:00:00 2001 From: davidgolden Date: Tue, 2 May 2023 16:40:22 -0600 Subject: [PATCH 1/2] grab title from calendar and handle condition where tzid is not defined --- src/IcalParser.php | 11 ++++++- tests/cal/calendar_title.ics | 63 ++++++++++++++++++++++++++++++++++++ tests/cal/no_tzid.ics | 58 +++++++++++++++++++++++++++++++++ tests/calendar_title.phpt | 12 +++++++ tests/no_tzid_value.phpt | 13 ++++++++ 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 tests/cal/calendar_title.ics create mode 100644 tests/cal/no_tzid.ics create mode 100644 tests/calendar_title.phpt create mode 100644 tests/no_tzid_value.phpt diff --git a/src/IcalParser.php b/src/IcalParser.php index 8f56053..4305c09 100644 --- a/src/IcalParser.php +++ b/src/IcalParser.php @@ -10,6 +10,9 @@ class IcalParser { /** @var \DateTimeZone */ public $timezone; + /** @var string */ + public $title; + /** @var array */ public $data; @@ -382,7 +385,13 @@ private function parseRow($row) { // if this is an invalid timezone, then fallback to GMT. This logic may not be right! $this->timezone = new \DateTimeZone("GMT"); } - } + } else if ($key === "TZOFFSETFROM" && !$this->timezone) { + $this->timezone = new \DateTimezone($value); + } + + if ($key === "X-WR-CALNAME") { + $this->title = $value; + } // have some middle part ? if ($middle && preg_match_all('#(?[^=;]+)=(?[^;]+)#', $middle, $matches, PREG_SET_ORDER)) { diff --git a/tests/cal/calendar_title.ics b/tests/cal/calendar_title.ics new file mode 100644 index 0000000..dcaef06 --- /dev/null +++ b/tests/cal/calendar_title.ics @@ -0,0 +1,63 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:School Board +X-WR-TIMEZONE:America/Chicago +X-WR-CALDESC: +BEGIN:VTIMEZONE +TZID:America/Chicago +X-LIC-LOCATION:America/Chicago +BEGIN:DAYLIGHT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +TZNAME:CDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +TZNAME:CST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=America/Chicago:20160808T180000 +DTEND;TZID=America/Chicago:20160808T203000 +DTSTAMP:20160718T160226Z +UID:kpgr4590djmi89l3sv51gmalrc@google.com +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=School + Board;X-NUM-GUESTS=0:mailto:aliceisd.net_7hatmpq9uek3a1h6if0r0sn5bc@group. + calendar.google.com +RECURRENCE-ID;TZID=America/Chicago:20160808T183000 +CREATED:20150715T183809Z +DESCRIPTION: +LAST-MODIFIED:20160718T155538Z +LOCATION: +SEQUENCE:1 +STATUS:CONFIRMED +SUMMARY:Regular Board Meeting Modified +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=America/Chicago:20150810T183000 +DTEND;TZID=America/Chicago:20150810T210000 +RRULE:FREQ=MONTHLY;UNTIL=20180709T233000Z;BYDAY=2MO +DTSTAMP:20160718T160226Z +UID:kpgr4590djmi89l3sv51gmalrc@google.com +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=School + Board;X-NUM-GUESTS=0:mailto:aliceisd.net_7hatmpq9uek3a1h6if0r0sn5bc@group. + calendar.google.com +CREATED:20150715T183809Z +DESCRIPTION: +LAST-MODIFIED:20160718T155538Z +LOCATION: +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Regular Board Meeting +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR diff --git a/tests/cal/no_tzid.ics b/tests/cal/no_tzid.ics new file mode 100644 index 0000000..e9477e7 --- /dev/null +++ b/tests/cal/no_tzid.ics @@ -0,0 +1,58 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:School Board +X-WR-CALDESC: +BEGIN:VTIMEZONE +BEGIN:DAYLIGHT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=America/Chicago:20160808T180000 +DTEND;TZID=America/Chicago:20160808T203000 +DTSTAMP:20160718T160226Z +UID:kpgr4590djmi89l3sv51gmalrc@google.com +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=School + Board;X-NUM-GUESTS=0:mailto:aliceisd.net_7hatmpq9uek3a1h6if0r0sn5bc@group. + calendar.google.com +RECURRENCE-ID;TZID=America/Chicago:20160808T183000 +CREATED:20150715T183809Z +DESCRIPTION: +LAST-MODIFIED:20160718T155538Z +LOCATION: +SEQUENCE:1 +STATUS:CONFIRMED +SUMMARY:Regular Board Meeting Modified +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=America/Chicago:20150810T183000 +DTEND;TZID=America/Chicago:20150810T210000 +RRULE:FREQ=MONTHLY;UNTIL=20180709T233000Z;BYDAY=2MO +DTSTAMP:20160718T160226Z +UID:kpgr4590djmi89l3sv51gmalrc@google.com +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=School + Board;X-NUM-GUESTS=0:mailto:aliceisd.net_7hatmpq9uek3a1h6if0r0sn5bc@group. + calendar.google.com +CREATED:20150715T183809Z +DESCRIPTION: +LAST-MODIFIED:20160718T155538Z +LOCATION: +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Regular Board Meeting +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR diff --git a/tests/calendar_title.phpt b/tests/calendar_title.phpt new file mode 100644 index 0000000..f2e6e82 --- /dev/null +++ b/tests/calendar_title.phpt @@ -0,0 +1,12 @@ + + */ +use Tester\Assert; + +require_once __DIR__ . '/../vendor/autoload.php'; +\Tester\Environment::setup(); + +$cal = new \om\IcalParser(); +$results = $cal->parseFile(__DIR__ . '/cal/calendar_title.ics'); +Assert::same('School Board', $cal->title); diff --git a/tests/no_tzid_value.phpt b/tests/no_tzid_value.phpt new file mode 100644 index 0000000..65ac994 --- /dev/null +++ b/tests/no_tzid_value.phpt @@ -0,0 +1,13 @@ + + */ +use Tester\Assert; + +require_once __DIR__ . '/../vendor/autoload.php'; +\Tester\Environment::setup(); + +//some clean timezone +$cal = new \om\IcalParser(); +$results = $cal->parseFile(__DIR__ . '/cal/no_tzid.ics'); +Assert::same('-06:00', $cal->timezone->getName()); From b89178fb07ccb95c007dd9d319bc995132fe04d0 Mon Sep 17 00:00:00 2001 From: davidgolden Date: Wed, 3 May 2023 07:32:30 -0600 Subject: [PATCH 2/2] if no tzid is provided (or an invalid one), but TZOFFSETTO is available, use that to build a timezone --- src/IcalParser.php | 17 +++++++++++++++-- tests/no_tzid_value.phpt | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/IcalParser.php b/src/IcalParser.php index 4305c09..7eeffe7 100644 --- a/src/IcalParser.php +++ b/src/IcalParser.php @@ -385,8 +385,21 @@ private function parseRow($row) { // if this is an invalid timezone, then fallback to GMT. This logic may not be right! $this->timezone = new \DateTimeZone("GMT"); } - } else if ($key === "TZOFFSETFROM" && !$this->timezone) { - $this->timezone = new \DateTimezone($value); + } else if ($key === "TZOFFSETTO" && + (!isset($this->timezone) || + $this->timezone->getName() === "GMT") && + preg_match("/[-+]?(?[\d]{2})(?[\d]{2})/", $value, $matches) + ) { + // if timezone is invalid and TZOFFSETTO is provided, use that to build a timezone + $seconds = intval($matches["hours"]) * 60 * 60 + intval($matches["minutes"]) * 60; + + if ($value[0] === "-") { + $seconds *= -1; + } + + // TZOFFSETTO provides local time offset when DST is not in operation, so I think this is right + $timezoneName = timezone_name_from_abbr("", $seconds, false); + $this->timezone = new \DateTimezone($timezoneName); } if ($key === "X-WR-CALNAME") { diff --git a/tests/no_tzid_value.phpt b/tests/no_tzid_value.phpt index 65ac994..65cd604 100644 --- a/tests/no_tzid_value.phpt +++ b/tests/no_tzid_value.phpt @@ -10,4 +10,4 @@ require_once __DIR__ . '/../vendor/autoload.php'; //some clean timezone $cal = new \om\IcalParser(); $results = $cal->parseFile(__DIR__ . '/cal/no_tzid.ics'); -Assert::same('-06:00', $cal->timezone->getName()); +Assert::same('America/New_York', $cal->timezone->getName());