diff --git a/src/IcalParser.php b/src/IcalParser.php index 8f56053..7eeffe7 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,26 @@ 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 === "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") { + $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..65cd604 --- /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('America/New_York', $cal->timezone->getName());