Umbau auf icalparser
neu: do_import, ungetestet.
This commit is contained in:
parent
e7b00abad8
commit
37878c0586
|
@ -41,19 +41,24 @@ function gcal_import_worker()
|
|||
global $wpdb;
|
||||
$table = $wpdb->prefix.GCAL_TABLE;
|
||||
$categories = $wpdb->get_results("SELECT gcal_category from $table");
|
||||
if (empty($categories) {
|
||||
error_log ("keine Einträge in $wpdb->prefix.GCAL_TABLE gefunden.");
|
||||
return (0);
|
||||
}
|
||||
$file = dirname (__FILE__) . '/categories.txt';
|
||||
file_put_contents ($file, var_export ($categories, TRUE));
|
||||
/*
|
||||
|
||||
foreach ( $categories as $category) {
|
||||
error_log ("found category $category");
|
||||
gcal_import_process_category($category);
|
||||
}
|
||||
*/
|
||||
|
||||
error_log ("gcal_import_worker finished", 0);
|
||||
}
|
||||
|
||||
// include ('CalFileParser.php');
|
||||
|
||||
|
||||
|
||||
|
||||
function gcal_import_process_category($category) {
|
||||
global $wpdb;
|
||||
|
@ -61,13 +66,17 @@ function gcal_import_process_category($category) {
|
|||
$query = "SELECT gcal_link from $table WHERE gcal_category = '$category' AND gcal_active = '1' ;";
|
||||
$link = $wpdb->query($query);
|
||||
error_log ("found active link $link for category $category");
|
||||
$cal = new CalFileParser();
|
||||
// will this also work with a proxy? After all, it does a file_get_contents internally.
|
||||
$result = $cal->parse($link);
|
||||
$file = dirname (__FILE__) . '/$category-array.txt';
|
||||
file_put_contents ($file, var_dump ($result));
|
||||
|
||||
|
||||
// jetzt haben wir category und link.
|
||||
// erst alle termine von category löschen
|
||||
$post_ids = $wpdb->get_results("SELECT Id from $wpdb->prefix.postmeta where
|
||||
key = '_gcal_category' AND key_value = '$category'");
|
||||
foreach ($post_ids as $post_id) {
|
||||
wp_trash_post($post_id);
|
||||
}
|
||||
// jetzt die neuen Posts anlegen
|
||||
gcal_import_do_import($category, $link);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -68,13 +68,15 @@ function example_add_cron_interval( $schedules ) {
|
|||
* - gcal_link - the public or private .ics link
|
||||
* - gcal_veranstalter - ?
|
||||
* - gcal_active - flag if a calendar is active or not. Default active.
|
||||
*
|
||||
* Since there is no install hook in WP, we will use the activation hook for both.
|
||||
*/
|
||||
|
||||
function gcal_import_activate()
|
||||
{
|
||||
error_log ("gcal_import_activate started");
|
||||
global $wpdb;
|
||||
// CREATE will produce an error if the table exists already.
|
||||
// CREATE table if it does not exist already.
|
||||
$table = $wpdb->prefix.GCAL_TABLE;
|
||||
$query = "CREATE TABLE IF NOT EXISTS $table (
|
||||
id INT(9) NOT NULL AUTO_INCREMENT,
|
||||
|
@ -96,10 +98,15 @@ function gcal_import_activate()
|
|||
|
||||
|
||||
// do it once now! Won't work if the table hasn't been populated yet.
|
||||
$result = $wpdb->query("SELECT gcal_category FROM $table");
|
||||
if ($result != 0) {
|
||||
gcal_import_worker;
|
||||
}
|
||||
// and start the scheduler;
|
||||
// in production, we will do this hourly.
|
||||
if ( ! wp_next_scheduled( 'gcal_import_worker_hook' ) ) {
|
||||
wp_schedule_event( time(), 'five_minutes', 'gcal_import_worker_hook' );
|
||||
// wp_schedule_event( time(), 'hourly', 'gcal_import_worker_hook' );
|
||||
}
|
||||
error_log ("gcal_import_activate finished");
|
||||
|
||||
|
@ -109,7 +116,7 @@ register_activation_hook( __FILE__, 'gcal_import_activate' );
|
|||
|
||||
|
||||
/**
|
||||
* Deactivate unregisters the housekeeping function.
|
||||
* Deactivate unregisters the scheduling function.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
|
@ -139,8 +146,7 @@ function gcal_import_uninstall()
|
|||
// gcal_import_deactivate;
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix.GCAL_TABLE;
|
||||
$query = "DROP TABLE $table ;";
|
||||
$wpdb->query($query);
|
||||
$wpdb->query("DROP TABLE $table");
|
||||
error_log ("gcal_import_uninstall finished");
|
||||
}
|
||||
|
||||
|
|
57
gcal_import_do_import.php
Normal file
57
gcal_import_do_import.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
function gcal_import_do_import($category, $link) {
|
||||
|
||||
$gcal_category = 'Neufahrn';
|
||||
my_post = [];
|
||||
|
||||
require_once dirname (__FILE__) . '/../../icalparser/src/IcalParser.php';
|
||||
require_once dirname (__FILE__) . '/../../icalparser/src/Recurrence.php';
|
||||
require_once dirname (__FILE__) . '/../../icalparser/src/Freq.php';
|
||||
require_once dirname (__FILE__) . '/../../icalparser/src/WindowsTimezones.php';
|
||||
|
||||
$cal = new \om\IcalParser();
|
||||
$results = $cal->parseFile(
|
||||
'https://calendar.google.com/calendar/ical/gruene.freising%40gmail.com/public/basic.ics'
|
||||
);
|
||||
|
||||
// we must set a current user because we may not be logged in.
|
||||
$user_id = 1;
|
||||
$user = get_user_by( 'id', $user_id );
|
||||
if( $user ) {
|
||||
wp_set_current_user( $user_id, $user->user_login );
|
||||
wp_set_auth_cookie( $user_id );
|
||||
}
|
||||
// and create a nonce.
|
||||
$gcal_nonce = wp_create_nonce();
|
||||
foreach ($cal->getSortedEvents() as $r) {
|
||||
// TODO: wenn DTEND in der Vergangenheit liegt, nicht mehr posten. Next.
|
||||
$my_post->ID = 0;
|
||||
$my_post->post_author = $user_id; // always admin to avoid permission things.
|
||||
$my_post->post_content = $r['DESCRIPTION'];
|
||||
$my_post->post_title = $r['SUMMARY'];
|
||||
$my_post->post_excerpt = substr ($r['DESCRIPTION'], 0, 160) . ' ...'; // first 160 chars of DESCRIPTION plus ' ...'
|
||||
$my_post->post_status = 'published';
|
||||
$my_post->post_category = $gcal_category; // muss noch hierher.
|
||||
$my_post->post_name = '' ; // sanitized title
|
||||
$my_post->post_type = 'termine';
|
||||
$my_post->visibility = 'public';
|
||||
$my_post->wpc_from = $r['DTSTART']; // muss noch ins richtige Format.
|
||||
$my_post->wpc_until = $r['DTEND'];
|
||||
// Jetzt den ort zerlegen und geocoden
|
||||
$my_post->geocity = gcal_geocity($r['LOCATION']);
|
||||
$my_post->geoshow = gcal_geoshow($r['LOCATION']);
|
||||
// geocoden
|
||||
$my_post->wpc_lat = gcal_latitude($r['LOCATION']);
|
||||
$my_post->wpc_lon = gcal_longitude($r['LOCATION']);
|
||||
$my_post->wpc_zoom = 10;
|
||||
$my_post->wpcalendar_noncename = $gcal_nonce; // tja. eines mit wp_create_nonce() erzeugen.
|
||||
// Jetzt alles nach $_POST kopieren
|
||||
$post_id = wp_write_post ();
|
||||
// error handling, wenn Fehler
|
||||
add_post_meta ($post_id, '_gcal_category', '$category'); // gemient ist die aus dem GCal abgeleitete cat.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
609
tests/Freq.php
Normal file
609
tests/Freq.php
Normal file
|
@ -0,0 +1,609 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
471
tests/IcalParser.php
Normal file
471
tests/IcalParser.php
Normal file
|
@ -0,0 +1,471 @@
|
|||
<?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 [];
|
||||
}
|
||||
|
||||
}
|
234
tests/Recurrence.php
Normal file
234
tests/Recurrence.php
Normal file
|
@ -0,0 +1,234 @@
|
|||
<?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');
|
||||
}
|
||||
}
|
210
tests/WindowsTimezones.php
Normal file
210
tests/WindowsTimezones.php
Normal file
|
@ -0,0 +1,210 @@
|
|||
<?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',
|
||||
];
|
Reference in a new issue