added OpenStreetMap wording

This commit is contained in:
Harald Milz 2019-04-06 07:41:50 +02:00
parent 01fea085b3
commit bae6f9001e
7 changed files with 2 additions and 1541 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
icalparser/*

View file

@ -40,7 +40,7 @@ Hinweis: kal3000-gcal-import nutzt für das Parsen von ICAL-Files und -Feeds das
3. Im Admin-Teil kann man das Zeitintervall einstellen, mit dem die Kalender synchronisiert werden. Standardeinstellung ist 60 Minuten. Bitte beachten, dass der Wordpress-Scheduler die Zeitintervalle nur ungefähr und abhängig von der Seitenaktivität einhält. 3. Im Admin-Teil kann man das Zeitintervall einstellen, mit dem die Kalender synchronisiert werden. Standardeinstellung ist 60 Minuten. Bitte beachten, dass der Wordpress-Scheduler die Zeitintervalle nur ungefähr und abhängig von der Seitenaktivität einhält.
4. Im Admin-Teil kann man das Geocoding aktivieren. Derzeit ist nur ein inoffizieller Weg über Google Maps verfügbar, den Google nicht gerne sieht. Das offizielle <a href="https://developers.google.com/maps/documentation/geocoding/start">Google-API</a> erfordert einen API-Key, der bei intensiver Nutzung nicht kostenlos ist. Auf die Google-Policy wird hingewiesen. Außerdem soll es irgendwann OpenStreetMap geben. 4. Im Admin-Teil kann man das Geocoding aktivieren. Derzeit ist nur ein inoffizieller Weg über Google Maps verfügbar, den Google nicht gerne sieht. Das offizielle <a href="https://developers.google.com/maps/documentation/geocoding/start">Google-API</a> erfordert einen API-Key, der bei intensiver Nutzung nicht kostenlos ist. Auf die Google-Policy wird hingewiesen. Außerdem ist OpenStreetMap verfügbar, aber es kann nicht sehr gut mit den Lokationen aus Google Maps umgehen zu können.
5. Speichern und fertig. 5. Speichern und fertig.

View file

@ -1,609 +0,0 @@
<?php
namespace om;
/**
* Class taken from https://github.com/coopTilleuls/intouch-iCalendar.git (Freq.php)
*
* @author PC Drew <pc@schoolblocks.com>
*/
/**
* A class to store Frequency-rules in. Will allow a easy way to find the
* last and next occurrence of the rule.
*
* No - this is so not pretty. But.. ehh.. You do it better, and I will
* gladly accept patches.
*
* Created by trail-and-error on the examples given in the RFC.
*
* TODO: Update to a better way of doing calculating the different options.
* Instead of only keeping track of the best of the current dates found
* it should instead keep a array of all the calculated dates within the
* period.
* This should fix the issues with multi-rule + multi-rule interference,
* and make it possible to implement the SETPOS rule.
* By pushing the next period onto the stack as the last option will
* (hopefully) remove the need for the awful simpleMode
*
* @author Morten Fangel (C) 2008
* @author Michael Kahn (C) 2013
* @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
*/
class Freq {
protected $weekdays = [
'MO' => 'monday', 'TU' => 'tuesday', 'WE' => 'wednesday', 'TH' => 'thursday', 'FR' => 'friday', 'SA' => 'saturday',
'SU' => 'sunday'
];
protected $knownRules = [
'month', 'weekno', 'day', 'monthday', 'yearday', 'hour', 'minute'
]; //others : 'setpos', 'second'
protected $ruleModifiers = ['wkst'];
protected $simpleMode = true;
protected $rules = ['freq' => 'yearly', 'interval' => 1];
protected $start = 0;
protected $freq = '';
protected $excluded; //EXDATE
protected $added; //RDATE
protected $cache; // getAllOccurrences()
/**
* Constructs a new Freqency-rule
*
* @param $rule string
* @param $start int Unix-timestamp (important : Need to be the start of Event)
* @param $excluded array of int (timestamps), see EXDATE documentation
* @param $added array of int (timestamps), see RDATE documentation
*/
public function __construct($rule, $start, $excluded = [], $added = []) {
$this->start = $start;
$this->excluded = [];
$rules = [];
foreach ($rule AS $k => $v) {
$this->rules[strtolower($k)] = $v;
}
if (isset($this->rules['until']) && is_string($this->rules['until'])) {
$this->rules['until'] = strtotime($this->rules['until']);
} else if ($this->rules['until'] instanceof \DateTime) {
$this->rules['until'] = $this->rules['until']->getTimestamp();
}
$this->freq = strtolower($this->rules['freq']);
foreach ($this->knownRules AS $rule) {
if (isset($this->rules['by' . $rule])) {
if ($this->isPrerule($rule, $this->freq)) {
$this->simpleMode = false;
}
}
}
if (!$this->simpleMode) {
if (!(isset($this->rules['byday']) || isset($this->rules['bymonthday']) || isset($this->rules['byyearday']))) {
$this->rules['bymonthday'] = date('d', $this->start);
}
}
//set until, and cache
if (isset($this->rules['count'])) {
$cache[$ts] = $ts = $this->start;
for ($n = 1; $n < $this->rules['count']; $n++) {
$ts = $this->findNext($ts);
$cache[$ts] = $ts;
}
$this->rules['until'] = $ts;
//EXDATE
if (!empty($excluded)) {
foreach ($excluded as $ts) {
unset($cache[$ts]);
}
}
//RDATE
if (!empty($added)) {
$cache = array_unique(array_merge(array_values($cache), $added));
asort($cache);
}
$this->cache = array_values($cache);
}
$this->excluded = $excluded;
$this->added = $added;
}
/**
* Returns all timestamps array(), build the cache if not made before
*
* @return array
*/
public function getAllOccurrences() {
if (empty($this->cache)) {
$cache = [];
//build cache
$next = $this->firstOccurrence();
while ($next) {
$cache[] = $next;
$next = $this->findNext($next);
}
if (!empty($this->added)) {
$cache = array_unique(array_merge($cache, $this->added));
asort($cache);
}
$this->cache = $cache;
}
return $this->cache;
}
/**
* Returns the previous (most recent) occurrence of the rule from the
* given offset
*
* @param int $offset
* @return int
*/
public function previousOccurrence($offset) {
if (!empty($this->cache)) {
$t2 = $this->start;
foreach ($this->cache as $ts) {
if ($ts >= $offset)
return $t2;
$t2 = $ts;
}
} else {
$ts = $this->start;
while (($t2 = $this->findNext($ts)) < $offset) {
if ($t2 == false) {
break;
}
$ts = $t2;
}
}
return $ts;
}
/**
* Returns the next occurrence of this rule after the given offset
*
* @param int $offset
* @return int
*/
public function nextOccurrence($offset) {
if ($offset < $this->start)
return $this->firstOccurrence();
return $this->findNext($offset);
}
/**
* Finds the first occurrence of the rule.
*
* @return int timestamp
*/
public function firstOccurrence() {
$t = $this->start;
if (in_array($t, $this->excluded))
$t = $this->findNext($t);
return $t;
}
/**
* Finds the absolute last occurrence of the rule from the given offset.
* Builds also the cache, if not set before...
*
* @return int timestamp
*/
public function lastOccurrence() {
//build cache if not done
$this->getAllOccurrences();
//return last timestamp in cache
return end($this->cache);
}
/**
* Calculates the next time after the given offset that the rule
* will apply.
*
* The approach to finding the next is as follows:
* First we establish a timeframe to find timestamps in. This is
* between $offset and the end of the period that $offset is in.
*
* We then loop though all the rules (that is a Prerule in the
* current freq.), and finds the smallest timestamp inside the
* timeframe.
*
* If we find something, we check if the date is a valid recurrence
* (with validDate). If it is, we return it. Otherwise we try to
* find a new date inside the same timeframe (but using the new-
* found date as offset)
*
* If no new timestamps were found in the period, we try in the
* next period
*
* @param int $offset
* @return int
*/
public function findNext($offset) {
if (!empty($this->cache)) {
foreach ($this->cache as $ts) {
if ($ts > $offset)
return $ts;
}
}
$debug = false;
//make sure the offset is valid
if ($offset === false || (isset($this->rules['until']) && $offset > $this->rules['until'])) {
if ($debug) echo 'STOP: ' . date('r', $offset) . "\n";
return false;
}
$found = true;
//set the timestamp of the offset (ignoring hours and minutes unless we want them to be
//part of the calculations.
if ($debug) echo 'O: ' . date('r', $offset) . "\n";
$hour = (in_array($this->freq, ['hourly', 'minutely']) && $offset > $this->start) ? date('H', $offset) : date(
'H', $this->start
);
$minute = (($this->freq === 'minutely' || isset($this->rules['byminute'])) && $offset > $this->start) ? date(
'i', $offset
) : date('i', $this->start);
$t = mktime($hour, $minute, date('s', $this->start), date('m', $offset), date('d', $offset), date('Y', $offset));
if ($debug) echo 'START: ' . date('r', $t) . "\n";
if ($this->simpleMode) {
if ($offset < $t) {
$ts = $t;
if ($ts && in_array($ts, $this->excluded))
$ts = $this->findNext($ts);
} else {
$ts = $this->findStartingPoint($t, $this->rules['interval'], false);
if (!$this->validDate($ts)) {
$ts = $this->findNext($ts);
}
}
return $ts;
}
//EOP needs to have the same TIME as START ($t)
$tO = new \DateTime('@'.$t, new \DateTimeZone('UTC'));
$eop = $this->findEndOfPeriod($offset);
$eopO = new \DateTime('@'.$eop, new \DateTimeZone('UTC'));
$eopO->setTime($tO->format('H'),$tO->format('i'),$tO->format('s'));
$eop = $eopO->getTimestamp();
unset($eopO);
unset($tO);
if ($debug) echo 'EOP: ' . date('r', $eop) . "\n";
foreach ($this->knownRules AS $rule) {
if ($found && isset($this->rules['by' . $rule])) {
if ($this->isPrerule($rule, $this->freq)) {
$subrules = explode(',', $this->rules['by' . $rule]);
$_t = null;
foreach ($subrules AS $subrule) {
$imm = call_user_func_array([$this, 'ruleBy' . $rule], [$subrule, $t]);
if ($imm === false) {
break;
}
if ($debug) echo strtoupper($rule) . ': ' . date(
'r', $imm
) . ' A: ' . ((int)($imm > $offset && $imm < $eop)) . "\n";
if ($imm > $offset && $imm <= $eop && ($_t == null || $imm < $_t)) {
$_t = $imm;
}
}
if ($_t !== null) {
$t = $_t;
} else {
$found = $this->validDate($t);
}
}
}
}
if ($offset < $this->start && $this->start < $t) {
$ts = $this->start;
} elseif ($found && ($t != $offset)) {
if ($this->validDate($t)) {
if ($debug) echo 'OK' . "\n";
$ts = $t;
} else {
if ($debug) echo 'Invalid' . "\n";
$ts = $this->findNext($t);
}
} else {
if ($debug) echo 'Not found' . "\n";
$ts = $this->findNext($this->findStartingPoint($offset, $this->rules['interval']));
}
if ($ts && in_array($ts, $this->excluded))
return $this->findNext($ts);
return $ts;
}
/**
* Finds the starting point for the next rule. It goes $interval
* 'freq' forward in time since the given offset
*
* @param int $offset
* @param int $interval
* @param boolean $truncate
* @return int
*/
private function findStartingPoint($offset, $interval, $truncate = true) {
$_freq = ($this->freq === 'daily') ? 'day__' : $this->freq;
$t = '+' . $interval . ' ' . substr($_freq, 0, -2) . 's';
if ($_freq === 'monthly' && $truncate) {
if ($interval > 1) {
$offset = strtotime('+' . ($interval - 1) . ' months ', $offset);
}
$t = '+' . (date('t', $offset) - date('d', $offset) + 1) . ' days';
}
$sp = strtotime($t, $offset);
if ($truncate) {
$sp = $this->truncateToPeriod($sp, $this->freq);
}
return $sp;
}
/**
* Finds the earliest timestamp posible outside this perioid
*
* @param int $offset
* @return int
*/
public function findEndOfPeriod($offset) {
return $this->findStartingPoint($offset, 1);
}
/**
* Resets the timestamp to the beginning of the
* period specified by freq
*
* Yes - the fall-through is on purpose!
*
* @param int $time
* @param int $freq
* @return int
*/
private function truncateToPeriod($time, $freq) {
$date = getdate($time);
switch ($freq) {
case 'yearly':
$date['mon'] = 1;
case 'monthly':
$date['mday'] = 1;
case 'daily':
$date['hours'] = 0;
case 'hourly':
$date['minutes'] = 0;
case 'minutely':
$date['seconds'] = 0;
break;
case 'weekly':
if (date('N', $time) == 1) {
$date['hours'] = 0;
$date['minutes'] = 0;
$date['seconds'] = 0;
} else {
$date = getdate(strtotime('last monday 0:00', $time));
}
break;
}
$d = mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year']);
return $d;
}
/**
* Applies the BYDAY rule to the given timestamp
*
* @param string $rule
* @param int $t
* @return int
*/
private function ruleByday($rule, $t) {
$dir = ($rule{0} == '-') ? -1 : 1;
$dir_t = ($dir == 1) ? 'next' : 'last';
$d = $this->weekdays[substr($rule, -2)];
$s = $dir_t . ' ' . $d . ' ' . date('H:i:s', $t);
if ($rule == substr($rule, -2)) {
if (date('l', $t) == ucfirst($d)) {
$s = 'today ' . date('H:i:s', $t);
}
$_t = strtotime($s, $t);
if ($_t == $t && in_array($this->freq, ['weekly', 'monthly', 'yearly'])) {
// Yes. This is not a great idea.. but hey, it works.. for now
$s = 'next ' . $d . ' ' . date('H:i:s', $t);
$_t = strtotime($s, $_t);
}
return $_t;
} else {
$_f = $this->freq;
if (isset($this->rules['bymonth']) && $this->freq === 'yearly') {
$this->freq = 'monthly';
}
if ($dir == -1) {
$_t = $this->findEndOfPeriod($t);
} else {
$_t = $this->truncateToPeriod($t, $this->freq);
}
$this->freq = $_f;
$c = preg_replace('/[^0-9]/', '', $rule);
$c = ($c == '') ? 1 : $c;
$n = $_t;
while ($c > 0) {
if ($dir == 1 && $c == 1 && date('l', $t) == ucfirst($d)) {
$s = 'today ' . date('H:i:s', $t);
}
$n = strtotime($s, $n);
$c--;
}
return $n;
}
}
private function ruleBymonth($rule, $t) {
$_t = mktime(date('H', $t), date('i', $t), date('s', $t), $rule, date('d', $t), date('Y', $t));
if ($t == $_t && isset($this->rules['byday'])) {
// TODO: this should check if one of the by*day's exists, and have a multi-day value
return false;
} else {
return $_t;
}
}
private function ruleBymonthday($rule, $t) {
if ($rule < 0) {
$rule = date('t', $t) + $rule + 1;
}
return mktime(date('H', $t), date('i', $t), date('s', $t), date('m', $t), $rule, date('Y', $t));
}
private function ruleByyearday($rule, $t) {
if ($rule < 0) {
$_t = $this->findEndOfPeriod();
$d = '-';
} else {
$_t = $this->truncateToPeriod($t, $this->freq);
$d = '+';
}
$s = $d . abs($rule - 1) . ' days ' . date('H:i:s', $t);
return strtotime($s, $_t);
}
private function ruleByweekno($rule, $t) {
if ($rule < 0) {
$_t = $this->findEndOfPeriod();
$d = '-';
} else {
$_t = $this->truncateToPeriod($t, $this->freq);
$d = '+';
}
$sub = (date('W', $_t) == 1) ? 2 : 1;
$s = $d . abs($rule - $sub) . ' weeks ' . date('H:i:s', $t);
$_t = strtotime($s, $_t);
return $_t;
}
private function ruleByhour($rule, $t) {
$_t = mktime($rule, date('i', $t), date('s', $t), date('m', $t), date('d', $t), date('Y', $t));
return $_t;
}
private function ruleByminute($rule, $t) {
$_t = mktime(date('h', $t), $rule, date('s', $t), date('m', $t), date('d', $t), date('Y', $t));
return $_t;
}
private function validDate($t) {
if (isset($this->rules['until']) && $t > $this->rules['until']) {
return false;
}
if (in_array($t, $this->excluded)) {
return false;
}
if (isset($this->rules['bymonth'])) {
$months = explode(',', $this->rules['bymonth']);
if (!in_array(date('m', $t), $months)) {
return false;
}
}
if (isset($this->rules['byday'])) {
$days = explode(',', $this->rules['byday']);
foreach ($days As $i => $k) {
$days[$i] = $this->weekdays[preg_replace('/[^A-Z]/', '', $k)];
}
if (!in_array(strtolower(date('l', $t)), $days)) {
return false;
}
}
if (isset($this->rules['byweekno'])) {
$weeks = explode(',', $this->rules['byweekno']);
if (!in_array(date('W', $t), $weeks)) {
return false;
}
}
if (isset($this->rules['bymonthday'])) {
$weekdays = explode(',', $this->rules['bymonthday']);
foreach ($weekdays As $i => $k) {
if ($k < 0) {
$weekdays[$i] = date('t', $t) + $k + 1;
}
}
if (!in_array(date('d', $t), $weekdays)) {
return false;
}
}
if (isset($this->rules['byhour'])) {
$hours = explode(',', $this->rules['byhour']);
if (!in_array(date('H', $t), $hours)) {
return false;
}
}
return true;
}
private function isPrerule($rule, $freq) {
if ($rule === 'year')
return false;
if ($rule === 'month' && $freq === 'yearly')
return true;
if ($rule === 'monthday' && in_array($freq, ['yearly', 'monthly']) && !isset($this->rules['byday']))
return true;
// TODO: is it faster to do monthday first, and ignore day if monthday exists? - prolly by a factor of 4..
if ($rule === 'yearday' && $freq === 'yearly')
return true;
if ($rule === 'weekno' && $freq === 'yearly')
return true;
if ($rule === 'day' && in_array($freq, ['yearly', 'monthly', 'weekly']))
return true;
if ($rule === 'hour' && in_array($freq, ['yearly', 'monthly', 'weekly', 'daily']))
return true;
if ($rule === 'minute')
return true;
return false;
}
}

View file

@ -1,471 +0,0 @@
<?php
namespace om;
/**
* Copyright (c) 2004-2015 Roman Ožana (http://www.omdesign.cz)
*
* @author Roman Ožana <ozana@omdesign.cz>
*/
class IcalParser {
/** @var \DateTimeZone */
public $timezone;
/** @var array */
public $data = [];
/** @var array */
protected $counters = [];
/** @var array */
private $windowsTimezones;
protected $arrayKeyMappings = [
'ATTACH' => 'ATTACHMENTS',
'EXDATE' => 'EXDATES',
'RDATE' => 'RDATES',
];
public function __construct() {
$this->windowsTimezones = require __DIR__ . '/WindowsTimezones.php'; // load Windows timezones from separate file
}
/**
* @param string $file
* @param null $callback
* @return array|null
* @throws \RuntimeException
* @throws \InvalidArgumentException
* @throws \Exception
*/
public function parseFile($file, $callback = null) {
if (!$handle = fopen($file, 'r')) {
throw new \RuntimeException('Can\'t open file' . $file . ' for reading');
}
fclose($handle);
return $this->parseString(file_get_contents($file), $callback);
}
/**
* @param string $string
* @param null $callback
* @param boolean $add if true the parsed string is added to existing data
* @return array|null
* @throws \InvalidArgumentException
* @throws \Exception
*/
public function parseString($string, $callback = null, $add = false) {
if ($add === false) {
// delete old data
$this->data = [];
$this->counters = [];
}
if (!preg_match('/BEGIN:VCALENDAR/', $string)) {
throw new \InvalidArgumentException('Invalid ICAL data format');
}
$section = 'VCALENDAR';
// Replace \r\n with \n
$string = str_replace("\r\n", "\n", $string);
// Unfold multi-line strings
$string = str_replace("\n ", '', $string);
foreach (explode("\n", $string) as $row) {
switch ($row) {
case 'BEGIN:DAYLIGHT':
case 'BEGIN:VALARM':
case 'BEGIN:VTIMEZONE':
case 'BEGIN:VFREEBUSY':
case 'BEGIN:VJOURNAL':
case 'BEGIN:STANDARD':
case 'BEGIN:VTODO':
case 'BEGIN:VEVENT':
$section = substr($row, 6);
$this->counters[$section] = isset($this->counters[$section]) ? $this->counters[$section] + 1 : 0;
continue 2; // while
break;
case 'END:VEVENT':
$section = substr($row, 4);
$currCounter = $this->counters[$section];
$event = $this->data[$section][$currCounter];
if (!empty($event['RECURRENCE-ID'])) {
$this->data['_RECURRENCE_IDS'][$event['RECURRENCE-ID']] = $event;
}
continue 2; // while
break;
case 'END:DAYLIGHT':
case 'END:VALARM':
case 'END:VTIMEZONE':
case 'END:VFREEBUSY':
case 'END:VJOURNAL':
case 'END:STANDARD':
case 'END:VTODO':
continue 2; // while
break;
case 'END:VCALENDAR':
$veventSection = 'VEVENT';
if (!empty($this->data[$veventSection])) {
foreach ($this->data[$veventSection] as $currCounter => $event) {
if (!empty($event['RRULE']) || !empty($event['RDATE'])) {
$recurrences = $this->parseRecurrences($event);
if (!empty($recurrences)) {
$this->data[$veventSection][$currCounter]['RECURRENCES'] = $recurrences;
}
if (!empty($event['UID'])) {
$this->data["_RECURRENCE_COUNTERS_BY_UID"][$event['UID']] = $currCounter;
}
}
}
}
continue 2; // while
break;
}
list($key, $middle, $value) = $this->parseRow($row);
if ($callback) {
// call user function for processing line
call_user_func($callback, $row, $key, $middle, $value, $section, $this->counters[$section]);
} else {
if ($section === 'VCALENDAR') {
$this->data[$key] = $value;
} else {
if (isset($this->arrayKeyMappings[$key])) {
// use an array since there can be multiple entries for this key. This does not
// break the current implementation--it leaves the original key alone and adds
// a new one specifically for the array of values.
$arrayKey = $this->arrayKeyMappings[$key];
$this->data[$section][$this->counters[$section]][$arrayKey][] = $value;
}
$this->data[$section][$this->counters[$section]][$key] = $value;
}
}
}
return ($callback) ? null : $this->data;
}
/**
* @param $row
* @return array
*/
private function parseRow($row) {
preg_match('#^([\w-]+);?([\w-]+="[^"]*"|.*?):(.*)$#i', $row, $matches);
$key = false;
$middle = null;
$value = null;
if ($matches) {
$key = $matches[1];
$middle = $matches[2];
$value = $matches[3];
$timezone = null;
if ($key === 'X-WR-TIMEZONE' || $key === 'TZID') {
if (preg_match('#(\w+/\w+)$#i', $value, $matches)) {
$value = $matches[1];
}
$value = $this->toTimezone($value);
$this->timezone = new \DateTimeZone($value);
}
// have some middle part ?
if ($middle && preg_match_all('#(?<key>[^=;]+)=(?<value>[^;]+)#', $middle, $matches, PREG_SET_ORDER)) {
$middle = [];
foreach ($matches as $match) {
if ($match['key'] === 'TZID') {
$match['value'] = trim($match['value'], "'\"");
$match['value'] = $this->toTimezone($match['value']);
try {
$middle[$match['key']] = $timezone = new \DateTimeZone($match['value']);
} catch (\Exception $e) {
$middle[$match['key']] = $match['value'];
}
} else if ($match['key'] === 'ENCODING') {
if ($match['value'] === 'QUOTED-PRINTABLE') {
$value = quoted_printable_decode($value);
}
}
}
}
}
// process simple dates with timezone
if (in_array($key, ['DTSTAMP', 'LAST-MODIFIED', 'CREATED', 'DTSTART', 'DTEND'], true)) {
try {
$value = new \DateTime($value, ($timezone ?: $this->timezone));
} catch (\Exception $e) {
$value = null;
}
} else if (in_array($key, ['EXDATE', 'RDATE'])) {
$values = [];
foreach (explode(',', $value) as $singleValue) {
try {
$values[] = new \DateTime($singleValue, ($timezone ?: $this->timezone));
} catch (\Exception $e) {
// pass
}
}
if (count($values) === 1) {
$value = $values[0];
} else {
$value = $values;
}
}
if ($key === 'RRULE' && preg_match_all('#(?<key>[^=;]+)=(?<value>[^;]+)#', $value, $matches, PREG_SET_ORDER)) {
$middle = null;
$value = [];
foreach ($matches as $match) {
if (in_array($match['key'], ['UNTIL'])) {
try {
$value[$match['key']] = new \DateTime($match['value'], ($timezone ?: $this->timezone));
} catch (\Exception $e) {
$value[$match['key']] = $match['value'];
}
} else {
$value[$match['key']] = $match['value'];
}
}
}
//split by comma, escape \,
if ($key === 'CATEGORIES') {
$value = preg_split('/(?<![^\\\\]\\\\),/', $value);
}
//implement 4.3.11 Text ESCAPED-CHAR
$text_properties = [
'CALSCALE', 'METHOD', 'PRODID', 'VERSION', 'CATEGORIES', 'CLASS', 'COMMENT', 'DESCRIPTION'
, 'LOCATION', 'RESOURCES', 'STATUS', 'SUMMARY', 'TRANSP', 'TZID', 'TZNAME', 'CONTACT', 'RELATED-TO', 'UID'
, 'ACTION', 'REQUEST-STATUS'
];
if (in_array($key, $text_properties) || strpos($key, 'X-') === 0) {
if (is_array($value)) {
foreach ($value as &$var) {
$var = strtr($var, ['\\\\' => '\\', '\\N' => "\n", '\\n' => "\n", '\\;' => ';', '\\,' => ',']);
}
} else {
$value = strtr($value, ['\\\\' => '\\', '\\N' => "\n", '\\n' => "\n", '\\;' => ';', '\\,' => ',']);
}
}
return [$key, $middle, $value];
}
/**
* @param $event
* @return array
* @throws \Exception
*/
public function parseRecurrences($event) {
$recurring = new Recurrence($event['RRULE']);
$exclusions = [];
$additions = [];
if (!empty($event['EXDATES'])) {
foreach ($event['EXDATES'] as $exDate) {
if (is_array($exDate)) {
foreach ($exDate as $singleExDate) {
$exclusions[] = $singleExDate->getTimestamp();
}
} else {
$exclusions[] = $exDate->getTimestamp();
}
}
}
if (!empty($event['RDATES'])) {
foreach ($event['RDATES'] as $rDate) {
if (is_array($rDate)) {
foreach ($rDate as $singleRDate) {
$additions[] = $singleRDate->getTimestamp();
}
} else {
$additions[] = $rDate->getTimestamp();
}
}
}
$until = $recurring->getUntil();
if ($until === false) {
//forever... limit to 3 years
$end = clone($event['DTSTART']);
$end->add(new \DateInterval('P3Y')); // + 3 years
$recurring->setUntil($end);
$until = $recurring->getUntil();
}
date_default_timezone_set($event['DTSTART']->getTimezone()->getName());
$frequency = new Freq($recurring->rrule, $event['DTSTART']->getTimestamp(), $exclusions, $additions);
$recurrenceTimestamps = $frequency->getAllOccurrences();
$recurrences = [];
foreach ($recurrenceTimestamps as $recurrenceTimestamp) {
$tmp = new \DateTime('now', $event['DTSTART']->getTimezone());
$tmp->setTimestamp($recurrenceTimestamp);
$recurrenceIDDate = $tmp->format('Ymd');
$recurrenceIDDateTime = $tmp->format('Ymd\THis');
if (empty($this->data['_RECURRENCE_IDS'][$recurrenceIDDate]) &&
empty($this->data['_RECURRENCE_IDS'][$recurrenceIDDateTime])) {
$gmtCheck = new \DateTime("now", new \DateTimeZone('UTC'));
$gmtCheck->setTimestamp($recurrenceTimestamp);
$recurrenceIDDateTimeZ = $gmtCheck->format('Ymd\THis\Z');
if (empty($this->data['_RECURRENCE_IDS'][$recurrenceIDDateTimeZ])) {
$recurrences[] = $tmp;
}
}
}
return $recurrences;
}
/**
* @return array
*/
public function getEvents() {
$events = [];
if (isset($this->data['VEVENT'])) {
for ($i = 0; $i < count($this->data['VEVENT']); $i++) {
$event = $this->data['VEVENT'][$i];
if (empty($event['RECURRENCES'])) {
if (!empty($event['RECURRENCE-ID']) && !empty($event['UID']) && isset($event['SEQUENCE'])) {
$modifiedEventUID = $event['UID'];
$modifiedEventRecurID = $event['RECURRENCE-ID'];
$modifiedEventSeq = intval($event['SEQUENCE'], 10);
if (isset($this->data["_RECURRENCE_COUNTERS_BY_UID"][$modifiedEventUID])) {
$counter = $this->data["_RECURRENCE_COUNTERS_BY_UID"][$modifiedEventUID];
$originalEvent = $this->data["VEVENT"][$counter];
if (isset($originalEvent['SEQUENCE'])) {
$originalEventSeq = intval($originalEvent['SEQUENCE'], 10);
$originalEventFormattedStartDate = $originalEvent['DTSTART']->format('Ymd\THis');
if ($modifiedEventRecurID === $originalEventFormattedStartDate && $modifiedEventSeq > $originalEventSeq) {
// this modifies the original event
$modifiedEvent = array_replace_recursive($originalEvent, $event);
$this->data["VEVENT"][$counter] = $modifiedEvent;
foreach ($events as $z => $event) {
if ($events[$z]['UID'] === $originalEvent['UID'] &&
$events[$z]['SEQUENCE'] === $originalEvent['SEQUENCE']) {
// replace the original event with the modified event
$events[$z] = $modifiedEvent;
break;
}
}
$event = null; // don't add this to the $events[] array again
} else if (!empty($originalEvent['RECURRENCES'])) {
for ($j = 0; $j < count($originalEvent['RECURRENCES']); $j++) {
$recurDate = $originalEvent['RECURRENCES'][$j];
$formattedStartDate = $recurDate->format('Ymd\THis');
if ($formattedStartDate === $modifiedEventRecurID) {
unset($this->data["VEVENT"][$counter]['RECURRENCES'][$j]);
$this->data["VEVENT"][$counter]['RECURRENCES'] = array_values($this->data["VEVENT"][$counter]['RECURRENCES']);
break;
}
}
}
}
}
}
if (!empty($event)) {
$events[] = $event;
}
} else {
$recurrences = $event['RECURRENCES'];
$event['RECURRING'] = true;
$event['DTEND'] = !empty($event['DTEND']) ? $event['DTEND'] : $event['DTSTART'];
$eventInterval = $event['DTSTART']->diff($event['DTEND']);
$firstEvent = true;
foreach ($recurrences as $j => $recurDate) {
$newEvent = $event;
if (!$firstEvent) {
unset($newEvent['RECURRENCES']);
$newEvent['DTSTART'] = $recurDate;
$newEvent['DTEND'] = clone($recurDate);
$newEvent['DTEND']->add($eventInterval);
}
$newEvent['RECURRENCE_INSTANCE'] = $j;
$events[] = $newEvent;
$firstEvent = false;
}
}
}
}
return $events;
}
/**
* Process timezone and return correct one...
*
* @param string $zone
* @return mixed|null
*/
private function toTimezone($zone) {
return isset($this->windowsTimezones[$zone]) ? $this->windowsTimezones[$zone] : $zone;
}
/**
* @return array
*/
public function getAlarms() {
return isset($this->data['VALARM']) ? $this->data['VALARM'] : [];
}
/**
* @return array
*/
public function getTimezones() {
return isset($this->data['VTIMEZONE']) ? $this->data['VTIMEZONE'] : [];
}
/**
* Return sorted event list as array
*
* @return array
*/
public function getSortedEvents() {
if ($events = $this->getEvents()) {
usort(
$events, function ($a, $b) {
return $a['DTSTART'] > $b['DTSTART'];
}
);
return $events;
}
return [];
}
/**
* @return array
*/
public function getReverseSortedEvents() {
if ($events = $this->getEvents()) {
usort(
$events, function ($a, $b) {
return $a['DTSTART'] < $b['DTSTART'];
}
);
return $events;
}
return [];
}
}

View file

@ -1,234 +0,0 @@
<?php
namespace om;
use \DateTime;
/**
* Class taken from https://github.com/coopTilleuls/intouch-iCalendar.git (Recurrence.php)
*
* A wrapper for recurrence rules in iCalendar. Parses the given line and puts the
* recurrence rules in the correct field of this object.
*
* See http://tools.ietf.org/html/rfc2445 for more information. Page 39 and onward contains more
* information on the recurrence rules themselves. Page 116 and onward contains
* some great examples which were often used for testing.
*
* @author Steven Oxley
* @author Michael Kahn (C) 2013
* @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
*/
class Recurrence {
public $rrule;
protected $freq;
protected $until;
protected $count;
protected $interval;
protected $bysecond;
protected $byminute;
protected $byhour;
protected $byday;
protected $bymonthday;
protected $byyearday;
protected $byweekno;
protected $bymonth;
protected $bysetpos;
protected $wkst;
/**
* A list of the properties that can have comma-separated lists for values.
*
* @var array
*/
protected $listProperties = [
'bysecond', 'byminute', 'byhour', 'byday', 'bymonthday',
'byyearday', 'byweekno', 'bymonth', 'bysetpos'
];
/**
* Creates an recurrence object with a passed in line. Parses the line.
*
* @param array $rrule an om\icalparser row array which will be parsed to get the
* desired information.
*/
public function __construct($rrule) {
$this->parseRrule($rrule);
}
/**
* Parses an 'RRULE' array and sets the member variables of this object.
* Expects a string that looks like this: 'FREQ=WEEKLY;INTERVAL=2;BYDAY=SU,TU,WE'
*
* @param $rrule
*/
protected function parseRrule($rrule) {
$this->rrule = $rrule;
//loop through the properties in the line and set their associated
//member variables
foreach ($this->rrule as $propertyName => $propertyValue) {
//need the lower-case name for setting the member variable
$propertyName = strtolower($propertyName);
//split up the list of values into an array (if it's a list)
if (in_array($propertyName, $this->listProperties, true)) {
$propertyValue = explode(',', $propertyValue);
}
$this->$propertyName = $propertyValue;
}
}
/**
* Set the $until member
*
* @param mixed timestamp (int) / Valid DateTime format (string)
*/
public function setUntil($ts) {
if ($ts instanceof DateTime) {
$dt = $ts;
} else if (is_int($ts)) {
$dt = new DateTime('@' . $ts);
} else {
$dt = new DateTime($ts);
}
$this->until = $dt->format('Ymd\THisO');
$this->rrule['until'] = $this->until;
}
/**
* Retrieves the desired member variable and returns it (if it's set)
*
* @param string $member name of the member variable
* @return mixed the variable value (if set), false otherwise
*/
protected function getMember($member) {
if (isset($this->$member)) {
return $this->$member;
}
return false;
}
/**
* Returns the frequency - corresponds to FREQ in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getFreq() {
return $this->getMember('freq');
}
/**
* Returns when the event will go until - corresponds to UNTIL in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getUntil() {
return $this->getMember('until');
}
/**
* Returns the count of the times the event will occur (should only appear if 'until'
* does not appear) - corresponds to COUNT in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getCount() {
return $this->getMember('count');
}
/**
* Returns the interval - corresponds to INTERVAL in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getInterval() {
return $this->getMember('interval');
}
/**
* Returns the bysecond part of the event - corresponds to BYSECOND in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getBySecond() {
return $this->getMember('bysecond');
}
/**
* Returns the byminute information for the event - corresponds to BYMINUTE in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getByMinute() {
return $this->getMember('byminute');
}
/**
* Corresponds to BYHOUR in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getByHour() {
return $this->getMember('byhour');
}
/**
*Corresponds to BYDAY in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getByDay() {
return $this->getMember('byday');
}
/**
* Corresponds to BYMONTHDAY in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getByMonthDay() {
return $this->getMember('bymonthday');
}
/**
* Corresponds to BYYEARDAY in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getByYearDay() {
return $this->getMember('byyearday');
}
/**
* Corresponds to BYWEEKNO in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getByWeekNo() {
return $this->getMember('byweekno');
}
/**
* Corresponds to BYMONTH in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getByMonth() {
return $this->getMember('bymonth');
}
/**
* Corresponds to BYSETPOS in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getBySetPos() {
return $this->getMember('bysetpos');
}
/**
* Corresponds to WKST in RFC 2445.
*
* @return mixed string if the member has been set, false otherwise
*/
public function getWkst() {
return $this->getMember('wkst');
}
}

View file

@ -1,210 +0,0 @@
<?php
/**
* List of Windows Timezones
*/
return [
'Dateline Standard Time' => 'Etc/GMT+12',
'(UTC-12:00) International Date Line West' => 'Etc/GMT+12',
'UTC-11' => 'Etc/GMT+11',
'(UTC-11:00) Coordinated Universal Time -11' => 'Etc/GMT+11',
'Hawaiian Standard Time' => 'Pacific/Honolulu',
'(UTC-10:00) Hawaii' => 'Pacific/Honolulu',
'Alaskan Standard Time' => 'America/Anchorage',
'(UTC-09:00) Alaska' => 'America/Anchorage',
'Pacific Standard Time (Mexico)' => 'America/Santa_Isabel',
'(UTC-08:00) Baja California' => 'America/Santa_Isabel',
'Pacific Standard Time' => 'America/Los_Angeles',
'Pacific Time' => 'America/Los_Angeles',
'(UTC-08:00) Pacific Time (US and Canada)' => 'America/Los_Angeles',
'(UTC-08:00) Pacific Time (US & Canada)' => 'America/Los_Angeles',
'US Mountain Standard Time' => 'America/Phoenix',
'(UTC-07:00) Arizona' => 'America/Phoenix',
'Mountain Standard Time (Mexico)' => 'America/Chihuahua',
'(UTC-07:00) Chihuahua, La Paz, Mazatlan' => 'America/Chihuahua',
'Mountain Standard Time' => 'America/Denver',
'Mountain Time' => 'America/Denver',
'(UTC-07:00) Mountain Time (US and Canada)' => 'America/Denver',
'(UTC-07:00) Mountain Time (US & Canada)' => 'America/Denver',
'Central America Standard Time' => 'America/Guatemala',
'(UTC-06:00) Central America' => 'America/Guatemala',
'Central Standard Time' => 'America/Chicago',
'Central Time' => 'America/Chicago',
'(UTC-06:00) Central Time (US and Canada)' => 'America/Chicago',
'(UTC-06:00) Central Time (US & Canada)' => 'America/Chicago',
'Central Standard Time (Mexico)' => 'America/Mexico_City',
'(UTC-06:00) Guadalajara, Mexico City, Monterrey' => 'America/Mexico_City',
'Canada Central Standard Time' => 'America/Regina',
'(UTC-06:00) Saskatchewan' => 'America/Regina',
'SA Pacific Standard Time' => 'America/Bogota',
'(UTC-05:00) Bogota, Lima, Quito' => 'America/Bogota',
'Eastern Standard Time' => 'America/New_York',
'Eastern Time' => 'America/New_York',
'(UTC-05:00) Eastern Time (US and Canada)' => 'America/New_York',
'(UTC-05:00) Eastern Time (US & Canada)' => 'America/New_York',
'US Eastern Standard Time' => 'America/Indianapolis',
'(UTC-05:00) Indiana (East)' => 'America/Indianapolis',
'Venezuela Standard Time' => 'America/Caracas',
'(UTC-04:30) Caracas' => 'America/Caracas',
'Paraguay Standard Time' => 'America/Asuncion',
'(UTC-04:00) Asuncion' => 'America/Asuncion',
'Atlantic Standard Time' => 'America/Halifax',
'(UTC-04:00) Atlantic Time (Canada)' => 'America/Halifax',
'Central Brazilian Standard Time' => 'America/Cuiaba',
'(UTC-04:00) Cuiaba' => 'America/Cuiaba',
'SA Western Standard Time' => 'America/La_Paz',
'(UTC-04:00) Georgetown, La Paz, Manaus, San Juan' => 'America/La_Paz',
'Pacific SA Standard Time' => 'America/Santiago',
'(UTC-04:00) Santiago' => 'America/Santiago',
'Newfoundland Standard Time' => 'America/St_Johns',
'(UTC-03:30) Newfoundland' => 'America/St_Johns',
'E. South America Standard Time' => 'America/Sao_Paulo',
'(UTC-03:00) Brasilia' => 'America/Sao_Paulo',
'Argentina Standard Time' => 'America/Buenos_Aires',
'(UTC-03:00) Buenos Aires' => 'America/Buenos_Aires',
'SA Eastern Standard Time' => 'America/Cayenne',
'(UTC-03:00) Cayenne, Fortaleza' => 'America/Cayenne',
'Greenland Standard Time' => 'America/Godthab',
'(UTC-03:00) Greenland' => 'America/Godthab',
'Montevideo Standard Time' => 'America/Montevideo',
'(UTC-03:00) Montevideo' => 'America/Montevideo',
'Bahia Standard Time' => 'America/Bahia',
'UTC-02' => 'Etc/GMT+2',
'(UTC-02:00) Coordinated Universal Time -02' => 'Etc/GMT+2',
'Azores Standard Time' => 'Atlantic/Azores',
'(UTC-01:00) Azores' => 'Atlantic/Azores',
'Cape Verde Standard Time' => 'Atlantic/Cape_Verde',
'(UTC-01:00) Cabo Verde Is.' => 'Atlantic/Cape_Verde',
'Morocco Standard Time' => 'Africa/Casablanca',
'(UTC) Casablanca' => 'Africa/Casablanca',
'UTC' => 'Etc/GMT',
'Microsoft/Utc' => 'Etc/GMT',
'GMT Standard Time' => 'Europe/London',
'(UTC) Dublin, Edinburgh, Lisbon, London' => 'Europe/London',
'Greenwich Standard Time' => 'Atlantic/Reykjavik',
'(UTC) Monrovia, Reykjavik' => 'Atlantic/Reykjavik',
'W. Europe Standard Time' => 'Europe/Berlin',
'(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin',
'Central Europe Standard Time' => 'Europe/Budapest',
'(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague' => 'Europe/Budapest',
'Romance Standard Time' => 'Europe/Paris',
'(UTC+01:00) Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris',
'Central European Standard Time' => 'Europe/Warsaw',
'(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb' => 'Europe/Warsaw',
'W. Central Africa Standard Time' => 'Africa/Lagos',
'(UTC+01:00) West Central Africa' => 'Africa/Lagos',
'Namibia Standard Time' => 'Africa/Windhoek',
'(UTC+01:00) Windhoek' => 'Africa/Windhoek',
'GTB Standard Time' => 'Europe/Bucharest',
'(UTC+02:00) Athens, Bucharest' => 'Europe/Bucharest',
'Middle East Standard Time' => 'Asia/Beirut',
'(UTC+02:00) Beirut' => 'Asia/Beirut',
'Egypt Standard Time' => 'Africa/Cairo',
'(UTC+02:00) Cairo' => 'Africa/Cairo',
'Syria Standard Time' => 'Asia/Damascus',
'(UTC+02:00) Damascus' => 'Asia/Damascus',
'South Africa Standard Time' => 'Africa/Johannesburg',
'(UTC+02:00) Harare, Pretoria' => 'Africa/Johannesburg',
'FLE Standard Time' => 'Europe/Kiev',
'(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius' => 'Europe/Kiev',
'Turkey Standard Time' => 'Europe/Istanbul',
'(UTC+02:00) Istanbul' => 'Europe/Istanbul',
'Israel Standard Time' => 'Asia/Jerusalem',
'(UTC+02:00) Jerusalem' => 'Asia/Jerusalem',
'Libya Standard Time' => 'Africa/Tripoli',
'Jordan Standard Time' => 'Asia/Amman',
'(UTC+02:00) Amman' => 'Asia/Amman',
'Arabic Standard Time' => 'Asia/Baghdad',
'(UTC+03:00) Baghdad' => 'Asia/Baghdad',
'Kaliningrad Standard Time' => 'Europe/Kaliningrad',
'(UTC+03:00) Kaliningrad' => 'Europe/Kaliningrad',
'Arab Standard Time' => 'Asia/Riyadh',
'(UTC+03:00) Kuwait, Riyadh' => 'Asia/Riyadh',
'E. Africa Standard Time' => 'Africa/Nairobi',
'(UTC+03:00) Nairobi' => 'Africa/Nairobi',
'Iran Standard Time' => 'Asia/Tehran',
'(UTC+03:30) Tehran' => 'Asia/Tehran',
'Arabian Standard Time' => 'Asia/Dubai',
'(UTC+04:00) Abu Dhabi, Muscat' => 'Asia/Dubai',
'Azerbaijan Standard Time' => 'Asia/Baku',
'(UTC+04:00) Baku' => 'Asia/Baku',
'Russian Standard Time' => 'Europe/Moscow',
'(UTC+04:00) Moscow, St. Petersburg, Volgograd' => 'Europe/Moscow',
'Mauritius Standard Time' => 'Indian/Mauritius',
'(UTC+04:00) Port Louis' => 'Indian/Mauritius',
'Georgian Standard Time' => 'Asia/Tbilisi',
'(UTC+04:00) Tbilisi' => 'Asia/Tbilisi',
'Caucasus Standard Time' => 'Asia/Yerevan',
'(UTC+04:00) Yerevan' => 'Asia/Yerevan',
'Afghanistan Standard Time' => 'Asia/Kabul',
'(UTC+04:30) Kabul' => 'Asia/Kabul',
'West Asia Standard Time' => 'Asia/Tashkent',
'(UTC+05:00) Tashkent' => 'Asia/Tashkent',
'Pakistan Standard Time' => 'Asia/Karachi',
'(UTC+05:00) Islamabad, Karachi' => 'Asia/Karachi',
'India Standard Time' => 'Asia/Calcutta',
'(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi' => 'Asia/Calcutta',
'Sri Lanka Standard Time' => 'Asia/Colombo',
'(UTC+05:30) Sri Jayawardenepura' => 'Asia/Colombo',
'Nepal Standard Time' => 'Asia/Katmandu',
'(UTC+05:45) Kathmandu' => 'Asia/Katmandu',
'Central Asia Standard Time' => 'Asia/Almaty',
'(UTC+06:00) Astana' => 'Asia/Almaty',
'Bangladesh Standard Time' => 'Asia/Dhaka',
'(UTC+06:00) Dhaka' => 'Asia/Dhaka',
'Ekaterinburg Standard Time' => 'Asia/Yekaterinburg',
'(UTC+06:00) Ekaterinburg' => 'Asia/Yekaterinburg',
'Myanmar Standard Time' => 'Asia/Rangoon',
'(UTC+06:30) Yangon (Rangoon)' => 'Asia/Rangoon',
'SE Asia Standard Time' => 'Asia/Bangkok',
'(UTC+07:00) Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok',
'N. Central Asia Standard Time' => 'Asia/Novosibirsk',
'(UTC+07:00) Novosibirsk' => 'Asia/Novosibirsk',
'China Standard Time' => 'Asia/Shanghai',
'(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi' => 'Asia/Shanghai',
'North Asia Standard Time' => 'Asia/Krasnoyarsk',
'(UTC+08:00) Krasnoyarsk' => 'Asia/Krasnoyarsk',
'Singapore Standard Time' => 'Asia/Singapore',
'(UTC+08:00) Kuala Lumpur, Singapore' => 'Asia/Singapore',
'W. Australia Standard Time' => 'Australia/Perth',
'(UTC+08:00) Perth' => 'Australia/Perth',
'Taipei Standard Time' => 'Asia/Taipei',
'(UTC+08:00) Taipei' => 'Asia/Taipei',
'Ulaanbaatar Standard Time' => 'Asia/Ulaanbaatar',
'(UTC+08:00) Ulaanbaatar' => 'Asia/Ulaanbaatar',
'North Asia East Standard Time' => 'Asia/Irkutsk',
'(UTC+09:00) Irkutsk' => 'Asia/Irkutsk',
'Tokyo Standard Time' => 'Asia/Tokyo',
'(UTC+09:00) Osaka, Sapporo, Tokyo' => 'Asia/Tokyo',
'Korea Standard Time' => 'Asia/Seoul',
'(UTC+09:00) Seoul' => 'Asia/Seoul',
'Cen. Australia Standard Time' => 'Australia/Adelaide',
'(UTC+09:30) Adelaide' => 'Australia/Adelaide',
'AUS Central Standard Time' => 'Australia/Darwin',
'(UTC+09:30) Darwin' => 'Australia/Darwin',
'E. Australia Standard Time' => 'Australia/Brisbane',
'(UTC+10:00) Brisbane' => 'Australia/Brisbane',
'AUS Eastern Standard Time' => 'Australia/Sydney',
'(UTC+10:00) Canberra, Melbourne, Sydney' => 'Australia/Sydney',
'West Pacific Standard Time' => 'Pacific/Port_Moresby',
'(UTC+10:00) Guam, Port Moresby' => 'Pacific/Port_Moresby',
'Tasmania Standard Time' => 'Australia/Hobart',
'(UTC+10:00) Hobart' => 'Australia/Hobart',
'Yakutsk Standard Time' => 'Asia/Yakutsk',
'(UTC+10:00) Yakutsk' => 'Asia/Yakutsk',
'Central Pacific Standard Time' => 'Pacific/Guadalcanal',
'(UTC+11:00) Solomon Is., New Caledonia' => 'Pacific/Guadalcanal',
'Vladivostok Standard Time' => 'Asia/Vladivostok',
'(UTC+11:00) Vladivostok' => 'Asia/Vladivostok',
'New Zealand Standard Time' => 'Pacific/Auckland',
'(UTC+12:00) Auckland, Wellington' => 'Pacific/Auckland',
'UTC+12' => 'Etc/GMT-12',
'(UTC+12:00) Coordinated Universal Time +12' => 'Etc/GMT-12',
'Fiji Standard Time' => 'Pacific/Fiji',
'(UTC+12:00) Fiji' => 'Pacific/Fiji',
'Magadan Standard Time' => 'Asia/Magadan',
'(UTC+12:00) Magadan' => 'Asia/Magadan',
'Tonga Standard Time' => 'Pacific/Tongatapu',
'(UTC+13:00) Nuku\'alofa' => 'Pacific/Tongatapu',
'Samoa Standard Time' => 'Pacific/Apia',
'(UTC-11:00)Samoa' => 'Pacific/Apia',
];

View file

@ -1,16 +0,0 @@
<?php
echo "<ul>";
require_once __DIR__ . '/../vendor/autoload.php';
$cal = new \om\IcalParser();
$results = $cal->parseFile(
'https://calendar.google.com/calendar/ical/gruene.freising%40gmail.com/public/basic.ics'
);
foreach ($cal->getSortedEvents() as $r) {
echo sprintf(' <li>%s - %s</li>' . PHP_EOL, $r['DTSTART']->format('j.n.Y'), $r['SUMMARY']);
}
echo "</ul>";
?>