Add iCal functionality

This commit is contained in:
o-psi
2024-02-08 12:59:36 -06:00
parent 50b2c3ad6c
commit e2392c3c6c
11 changed files with 2749 additions and 5 deletions

View File

@@ -0,0 +1,568 @@
<?php
/**
* date.php - date helper class
*
* @package ZapCalLib
* @author Dan Cogliano <http://zcontent.net>
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
* @link http://icalendar.org/php-library.html
*/
// No direct access
defined('_ZAPCAL') or die( 'Restricted access' );
/**
* Zap Calendar Date Helper Class
*
* Helper class for various date functions
*/
class ZDateHelper {
/**
* Find the number of days in a month
*
* @param int $month Month is between 1 and 12 inclusive
*
* @param int $year is between 1 and 32767 inclusive
*
* @return int
*/
static function DayInMonth($month, $year) {
$daysInMonth = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
if ($month != 2) return $daysInMonth[$month - 1];
return (checkdate($month, 29, $year)) ? 29 : 28;
}
/**
* Is given date today?
*
* @param int $date date in Unix timestamp format
*
* @param int $tzid PHP recognized timezone (default is UTC)
*
* @return bool
*/
static function isToday($date, $tzid = "UTC") {
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return gmdate('Y-m-d', $date) == gmdate('Y-m-d', $now);
}
/**
* Is given date before today?
*
* @param int $date date in Unix timestamp format
*
* @param int $tzid PHP recognized timezone (default is UTC)
*
* @return bool
*/
static function isBeforeToday($date, $tzid = "UTC"){
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return mktime(0,0,0,date('m',$now),date('d',$now),date('Y',$now)) >
mktime(0,0,0,date('m',$date),date('d',$date),date('Y',$now));
}
/**
* Is given date after today?
*
* @param int $date date in Unix timestamp format
*
* @param int $tzid PHP recognized timezone (default is UTC)
*
* @return bool
*/
static function isAfterToday($date, $tzid = "UTC"){
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return mktime(0,0,0,date('m',$now),date('d',$now),date('Y',$now)) <
mktime(0,0,0,date('m',$date),date('d',$date),date('Y',$now));
}
/**
* Is given date tomorrow?
*
* @param int $date date in Unix timestamp format
*
* @param int $tzid PHP recognized timezone (default is UTC)
*
* @return bool
*/
static function isTomorrow($date, $tzid = "UTC") {
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return gmdate('Y-m-d', $date) == gmdate('Y-m-d', $now + 60 * 60 * 24);
}
/**
* Is given date in the future?
*
* This routine differs from isAfterToday() in that isFuture() will
* return true for date-time values later in the same day.
*
* @param int $date date in Unix timestamp format
*
* @param int $tzid PHP recognized timezone (default is UTC)
*
* @return bool
*/
static function isFuture($date, $tzid = "UTC"){
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return $date > $now;
}
/**
* Is given date in the past?
*
* This routine differs from isBeforeToday() in that isPast() will
* return true for date-time values earlier in the same day.
*
* @param int $date date in Unix timestamp format
*
* @param int $tzid PHP recognized timezone (default is UTC)
*
* @return bool
*/
static function isPast($date, $tzid = "UTC") {
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return $date < $now;
}
/**
* Return current Unix timestamp in local timezone
*
* @param string $tzid PHP recognized timezone
*
* @return int
*/
static function now($tzid = "UTC"){
$dtz = new DateTimeZone($tzid);
$dt = new DateTime("now", $dtz);
$now = time() + $dtz->getOffset($dt);
return $now;
}
/**
* Is given date fall on a weekend?
*
* @param int $date Unix timestamp
*
* @return bool
*/
static function isWeekend($date) {
$dow = gmdate('w',$date);
return $dow == 0 || $dow == 6;
}
/**
* Format Unix timestamp to SQL date-time
*
* @param int $t Unix timestamp
*
* @return string
*/
static function toSqlDateTime($t = 0)
{
date_default_timezone_set('GMT');
if($t == 0)
return gmdate('Y-m-d H:i:s',self::now());
return gmdate('Y-m-d H:i:s', $t);
}
/**
* Format Unix timestamp to SQL date
*
* @param int $t Unix timestamp
*
* @return string
*/
static function toSqlDate($t = 0)
{
date_default_timezone_set('GMT');
if($t == 0)
return gmdate('Y-m-d',self::now());
return gmdate('Y-m-d', $t);
}
/**
* Format iCal date-time string to Unix timestamp
*
* @param string $datetime in iCal time format ( YYYYMMDD or YYYYMMDDTHHMMSS or YYYYMMDDTHHMMSSZ )
*
* @return int Unix timestamp
*/
static function fromiCaltoUnixDateTime($datetime) {
// first check format
$formats = array();
$formats[] = "/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]/";
$formats[] = "/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9]/";
$formats[] = "/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9]Z/";
$ok = false;
foreach($formats as $format){
if(preg_match($format,$datetime)){
$ok = true;
break;
}
}
if(!$ok)
return null;
$year = substr($datetime,0,4);
$month = substr($datetime,4,2);
$day = substr($datetime,6,2);
$hour = 0;
$minute = 0;
$second = 0;
if(strlen($datetime) > 8 && $datetime[8] == "T") {
$hour = substr($datetime,9,2);
$minute = substr($datetime,11,2);
$second = substr($datetime,13,2);
}
return gmmktime($hour, $minute, $second, $month, $day, $year);
}
/**
* Format Unix timestamp to iCal date-time string
*
* @param int $datetime Unix timestamp
*
* @return string
*/
static function fromUnixDateTimetoiCal($datetime){
date_default_timezone_set('GMT');
return gmdate("Ymd\THis",$datetime);
}
/**
* Convert iCal duration string to # of seconds
*
* @param string $duration iCal duration string
*
* return int
*/
static function iCalDurationtoSeconds($duration) {
$secs = 0;
if($duration[0] == "P") {
$duration = str_replace(array("H","M","S","T","D","W","P"),array("H,","M,","S,","","D,","W,",""),$duration);
$dur2 = explode(",",$duration);
foreach($dur2 as $dur){
$val=intval($dur);
if(strlen($dur) > 0){
switch($dur[strlen($dur) - 1]) {
case "H":
$secs += 60*60 * $val;
break;
case "M":
$secs += 60 * $val;
break;
case "S":
$secs += $val;
break;
case "D":
$secs += 60*60*24 * $val;
break;
case "W":
$secs += 60*60*24*7 * $val;
break;
}
}
}
}
return $secs;
}
/**
* Check if day falls within date range
*
* @param int $daystart start of day in Unix timestamp format
*
* @param int $begin Unix timestamp of starting date range
*
* @param int $end Unix timestamp of end date range
*
* @return bool
*/
static function inDay($daystart, $begin, $end)
{
//$dayend = $daystart + 60*60*24 - 60;
// add 1 day to determine end of day
// don't use 24 hours, since twice a year DST Sundays are 23 hours and 25 hours in length
// adding 1 day takes this into account
$dayend = self::addDate($daystart, 0,0,0,0,1,0);
$end = max($begin, $end); // $end can't be less than $begin
$inday =
($daystart <= $begin && $begin < $dayend)
||($daystart < $end && $end < $dayend)
||($begin <= $daystart && $end > $dayend)
;
return $inday;
}
/**
* Convert SQL date or date-time to Unix timestamp
*
* @param string $datetime SQL date or date-time
*
* @return int Unix date-time timestamp
*/
static function toUnixDate($datetime)
{
$year = substr($datetime,0,4);
$month = substr($datetime,5,2);
$day = substr($datetime,8,2);
return mktime(0, 0, 0, $month, $day, $year);
}
/**
* Convert SQL date or date-time to Unix date timestamp
*
* @param string $datetime SQL date or date-time
*
* @return int Unix timestamp
*/
static function toUnixDateTime($datetime)
{
// convert to absolute dates if neccessary
$datetime = self::getAbsDate($datetime);
$year = substr($datetime,0,4);
$month = substr($datetime,5,2);
$day = substr($datetime,8,2);
$hour = 0;
$minute = 0;
$second = 0;
if(strlen($datetime) > 10) {
$hour = substr($datetime,11,2);
$minute = substr($datetime,14,2);
$second = substr($datetime,17,2);
}
return gmmktime($hour, $minute, $second, $month, $day, $year);
}
/**
* Date math: add or substract from current date to get a new date
*
* @param int $date date to add or subtract from
*
* @param int $hour add or subtract hours from date
*
* @param int $min add or subtract minutes from date
*
* @param int $sec add or subtract seconds from date
*
* @param int $month add or subtract months from date
*
* @param int $day add or subtract days from date
*
* @param int $year add or subtract years from date
*
* @param string $tzid PHP recognized timezone (default is UTC)
*/
static function addDate($date, $hour, $min, $sec, $month, $day, $year, $tzid = "UTC") {
date_default_timezone_set($tzid);
$sqldate = self::toSQLDateTime($date);
$tdate = array();
$tdate["year"] = substr($sqldate,0,4);
$tdate["mon"] = substr($sqldate,5,2);
$tdate["mday"] = substr($sqldate,8,2);
$tdate["hours"] = substr($sqldate,11,2);
$tdate["minutes"] = substr($sqldate,14,2);
$tdate["seconds"] = substr($sqldate,17,2);
$newdate=mktime($tdate["hours"] + $hour, $tdate["minutes"] + $min, $tdate["seconds"] + $sec, $tdate["mon"] + $month, $tdate["mday"] + $day, $tdate["year"] + $year);
date_default_timezone_set("UTC");
//echo self::toSQLDateTime($date) . " => " . self::toSQLDateTime($newdate) . " ($hour:$min:$sec $month/$day/$year)<br/>\n";
return $newdate;
}
/**
* Date math: get date from week and day in specifiec month
*
* This routine finds actual dates for the second Tuesday of the month, last Friday of the month, etc.
* For second Tuesday, use $week = 1, $wday = 2
* for last Friday, use $week = -1, $wday = 5
*
* @param int $date Unix timestamp
*
* @param int $week week number, 0 is first week, -1 is last
*
* @param int $wday day of week, 0 is Sunday, 6 is Saturday
*
* @param string $tzid PHP supported timezone
*
* @return int Unix timestamp
*/
static function getDateFromDay($date, $week, $wday,$tzid="UTC") {
//echo "getDateFromDay(" . self::toSqlDateTime($date) . ",$week,$wday)<br/>\n";
// determine first day in month
$tdate = getdate($date);
$monthbegin = gmmktime(0,0,0, $tdate["mon"],1,$tdate["year"]);
$monthend = self::addDate($monthbegin, 0,0,0,1,-1,0,$tzid); // add 1 month and subtract 1 day
$day = self::addDate($date,0,0,0,0,1 - $tdate["mday"],0,$tzid);
$month = array(array());
while($day <= $monthend) {
$tdate=getdate($day);
$month[$tdate["wday"]][]=$day;
//echo self::toSQLDateTime($day) . "<br/>\n";
$day = self::addDate($day, 0,0,0,0,1,0,$tzid); // add 1 day
}
$dayinmonth=0;
if($week >= 0)
$dayinmonth = $month[$wday][$week];
else
$dayinmonth = $month[$wday][count($month[$wday]) - 1];
//echo "return " . self::toSQLDateTime($dayinmonth);
//exit;
return $dayinmonth;
}
/**
* Convert UTC date-time to local date-time
*
* @param string $sqldate SQL date-time string
*
* @param string $tzid PHP recognized timezone (default is "UTC")
*
* @return string SQL date-time string
*/
static function toLocalDateTime($sqldate, $tzid = "UTC" ){
try
{
$timezone = new DateTimeZone($tzid);
}
catch(Exception $e)
{
// bad time zone specified
return $sqldate;
}
$udate = self::toUnixDateTime($sqldate);
$daydatetime = new DateTime("@" . $udate);
$tzoffset = $timezone->getOffset($daydatetime);
return self::toSqlDateTime($udate + $tzoffset);
}
/**
* Convert local date-time to UTC date-time
*
* @param string $sqldate SQL date-time string
*
* @param string $tzid PHP recognized timezone (default is "UTC")
*
* @return string SQL date-time string
*/
static function toUTCDateTime($sqldate, $tzid = "UTC" ){
date_default_timezone_set("UTC");
try
{
$date = new DateTime($sqldate, $tzid);
}
catch(Exception $e)
{
// bad time zone specified
return $sqldate;
}
$offset = $date->getOffsetFromGMT();
if($offset >= 0)
$date->sub(new DateInterval("PT".$offset."S"));
else
$date->add(new DateInterval("PT".abs($offset)."S"));
return $date->toSql(true);
}
/**
* Convert from a relative date to an absolute date
*
* Examples of relative dates are "-2y" for 2 years ago, "18m"
* for 18 months after today. Relative date uses "y", "m" and "d" for
* year, month and day. Relative date can be combined into comma
* separated list, i.e., "-1y,-1d" for 1 year and 1 day ago.
*
* @param string $date relative date string (i.e. "1y" for 1 year from today)
*
* @param string $rdate reference date, or blank for current date (in SQL date-time format)
*
* @return string in SQL date-time format
*/
static function getAbsDate($date,$rdate = ""){
if(str_replace(array("y","m","d","h","n"),"",strtolower($date)) != strtolower($date)){
date_default_timezone_set("UTC");
if($rdate == "")
$udate = time();
else
$udate = self::toUnixDateTime($rdate);
$values=explode(",",strtolower($date));
$y = 0;
$m = 0;
$d = 0;
$h = 0;
$n = 0;
foreach($values as $value){
$rtype = substr($value,strlen($value)-1);
$rvalue = intval(substr($value,0,strlen($value) - 1));
switch($rtype){
case 'y':
$y = $rvalue;
break;
case 'm':
$m = $rvalue;
break;
case 'd':
$d = $rvalue;
break;
case 'h':
$h = $rvalue;
break;
case 'n':
$n = $rvalue;
break;
}
// for "-" values, move to start of day , otherwise, move to end of day
if($rvalue[0] == '-')
$udate = mktime(0,0,0,date('m',$udate),date('d',$udate),date('Y',$udate));
else
$udate = mktime(0,-1,0,date('m',$udate),date('d',$udate)+1,date('Y',$udate));
$udate = self::addDate($udate,$h,$n,0,$m,$d,$y);
}
$date = self::toSqlDateTime($udate);
}
return $date;
}
/**
* Format Unix timestamp to iCal date-time format
*
* @param int $datetime Unix timestamp
*
* @return string iCal date-time string
*/
static function toiCalDateTime($datetime = null){
date_default_timezone_set('UTC');
if($datetime == null)
$datetime = time();
return gmdate("Ymd\THis",$datetime);
}
/**
* Format Unix timestamp to iCal date format
*
* @param int $datetime Unix timestamp
*
* @return string iCal date-time string
*/
static function toiCalDate($datetime = null){
date_default_timezone_set('UTC');
if($datetime == null)
$datetime = time();
return gmdate("Ymd",$datetime);
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* framework.php - framework file
*
* @package ZapCalLib
* @author Dan Cogliano <http://zcontent.net>
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
* @link http://icalendar.org/php-library.html
*/
// No direct access
defined('_ZAPCAL') or die( 'Restricted access' );
/**
* set MAXYEAR to 2036 for 32 bit systems, can be higher for 64 bit systems
*
* @var integer
*/
define('_ZAPCAL_MAXYEAR', 2036);
/**
* set MAXREVENTS to maximum # of repeating events
*
* @var integer
*/
define('_ZAPCAL_MAXREVENTS', 5000);
require_once(_ZAPCAL_BASE . '/includes/date.php');
require_once(_ZAPCAL_BASE . '/includes/recurringdate.php');
require_once(_ZAPCAL_BASE . '/includes/ical.php');
require_once(_ZAPCAL_BASE . '/includes/timezone.php');

View File

@@ -0,0 +1,986 @@
<?php
/**
* ical.php create iCalendar data structure
*
* @package ZapCalLib
* @author Dan Cogliano <http://zcontent.net>
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
* @link http://icalendar.org/php-library.html
*/
// No direct access
defined('_ZAPCAL') or die( 'Restricted access' );
/**
* Object for storing an unfolded iCalendar line
*
* The ZCiCalDataNode class contains data from an unfolded iCalendar line
*
*/
class ZCiCalDataNode {
/**
* The name of the node
*
* @var string
*/
var $name = "";
/**
* Node parameters (before the colon ":")
*
* @var array
*/
var $parameter=array();
/**
* Node values (after the colon ":")
*
* @var array
*/
var $value=array();
/**
* Create an object from an unfolded iCalendar line
*
* @param string $line An unfolded iCalendar line
*
* @return void
*
*/
function __construct( $line ) {
//echo "ZCiCalDataNode($line)<br/>\n";
//separate line into parameters and value
// look for colon separating name or parameter and value
// first change any escaped colons temporarily to make it easier
$tline = str_replace("\\:", "`~", $line);
// see if first colon is inside a quoted string
$i = 0;
$datafind = false;
$inquotes = false;
while(!$datafind && ($i < strlen($tline))) {
//echo "$i: " . $tline[$i] . ", ord() = " . ord($tline[$i]) . "<br>\n";
if(!$inquotes && $tline[$i] == ':')
$datafind=true;
else{
$i += 1;
if(substr($tline,$i,1) == '"')
$inquotes = !$inquotes;
}
}
if($datafind){
$value = str_replace("`~","\\:",substr($line,$i+1));
// fix escaped characters (don't see double quotes in spec but Apple apparently uses it in iCal)
$value = str_replace(array('\\N' , '\\n', '\\"' ), array("\n", "\n" , '"'), $value);
$tvalue = str_replace("\\,", "`~", $value);
//echo "value: " . $tvalue . "<br>\n";
$tvalue = explode(",",$tvalue);
$value = str_replace("`~","\\,",$tvalue);
$this->value = $value;
}
$parameter = trim(substr($line,0,$i));
$parameter = str_replace("\\;", "`~", $parameter);
$parameters = explode(";", $parameter);
$parameters = str_replace("`~", "\\;", $parameters);
$this->name = array_shift($parameters);
foreach($parameters as $parameter){
$pos = strpos($parameter,"=");
if($pos > 0){
$param = substr($parameter,0,$pos);
$paramvalue = substr($parameter,$pos+1);
$tvalue = str_replace("\\,", "`~", $paramvalue);
//$tvalue = explode(",",$tvalue);
$paramvalue = str_replace("`~","\\,",$tvalue);
$this->parameter[strtolower($param)] = $paramvalue;
//$this->paramvalue[] = $paramvalue;
}
}
}
/**
* getName()
*
* Return the name of the object
*
* @return string
*/
function getName(){
return $this->name;
}
/**
* Get $ith parameter from array
* @param int $i
*
* @return var
*/
function getParameter($i){
return $this->parameter[$i];
}
/**
* Get parameter array
*
* @return array
*/
function getParameters(){
return $this->parameter;
}
/**
* Get comma separated values
*
* @return string
*/
function getValues(){
return implode(",",$this->value);
}
}
/**
* Object for storing a list of unfolded iCalendar lines (ZCiCalDataNode objects)
*
* @property object $parentnode Parent of this node
*
* @property array $child Array of children for this node
*
* @property data $data Array of data for this node
*
* @property object $next Next sibling of this node
*
* @property object $prev Previous sibling of this node
*/
class ZCiCalNode {
/**
* The name of the node
*
* @var string
*/
var $name="";
/**
* The parent of this node
*
* @var object
*/
var $parentnode=null;
/**
* Array of children for this node
*
* @var array
*/
var $child= array();
/**
* Array of $data for this node
*
* @var array
*/
var $data= array();
/**
* Next sibling of this node
*
* @var object
*/
var $next=null;
/**
* Previous sibling of this node
*
* @var object
*/
var $prev=null;
/**
* Create ZCiCalNode
*
* @param string $_name Name of node
*
* @param object $_parent Parent node for this node
*
* @param bool $first Is this the first child for this parent?
*/
function __construct( $_name, & $_parent, $first = false) {
$this->name = $_name;
$this->parentnode = $_parent;
if($_parent != null){
if(count($this->parentnode->child) > 0) {
if($first)
{
$first = & $this->parentnode->child[0];
$first->prev = & $this;
$this->next = & $first;
}
else
{
$prev =& $this->parentnode->child[count($this->parentnode->child)-1];
$prev->next =& $this;
$this->prev =& $prev;
}
}
if($first)
{
array_unshift($this->parentnode->child, $this);
}
else
{
$this->parentnode->child[] =& $this;
}
}
/*
echo "creating " . $this->getName();
if($_parent != null)
echo " child of " . $_parent->getName() . "/" . count($this->parentnode->child);
echo "<br/>";
*/
}
/**
* Return the name of the object
*
* @return string
*/
function getName() {
return $this->name;
}
/**
* Add node to list
*
* @param object $node
*
*/
function addNode($node) {
if(array_key_exists($node->getName(), $this->data))
{
if(!is_array($this->data[$node->getName()]))
{
$this->data[$node->getName()] = array($this->data[$node->getName()]);
}
$this->data[$node->getName()][] = $node;
}
else
{
$this->data[$node->getName()] = $node;
}
}
/**
* Get Attribute
*
* @param int $i array id of attribute to get
*
* @return string
*/
function getAttrib($i) {
return $this->attrib[$i];
}
/**
* Set Attribute
*
* @param string $value value of attribute to set
*
*/
function setAttrib($value) {
$this->attrib[] = $value;
}
/**
* Get the parent object of this object
*
* @return object parent of this object
*/
function &getParent() {
return $this->parentnode;
}
/**
* Get the first child of this object
*
* @return object The first child
*/
function &getFirstChild(){
static $nullguard = null;
if(count($this->child) > 0) {
//echo "moving from " . $this->getName() . " to " . $this->child[0]->getName() . "<br/>";
return $this->child[0];
}
else
return $nullguard;
}
/**
* Print object tree in HTML for debugging purposes
*
* @param object $node select part of tree to print, or leave blank for full tree
*
* @param int $level Level of recursion (usually leave this blank)
*
* @return string - HTML formatted display of object tree
*/
function printTree(& $node=null, $level=1){
$level += 1;
$html = "";
if($node == null)
$node = $this->parentnode;
if($level > 5)
{
die("levels nested too deep<br/>\n");
//return;
}
for($i = 0 ; $i < $level; $i ++)
$html .= "+";
$html .= $node->getName() . "<br/>\n";
foreach ($node->child as $c){
$html .= $node->printTree($c,$level);
}
$level -= 1;
return $html;
}
/**
* export tree to icalendar format
*
* @param object $node Top level node to export
*
* @param int $level Level of recursion (usually leave this blank)
*
* @return string iCalendar formatted output
*/
function export(& $node=null, $level=0){
$txtstr = "";
if($node == null)
$node = $this;
if($level > 5)
{
//die("levels nested too deep<br/>\n");
throw new Exception("levels nested too deep");
}
$txtstr .= "BEGIN:" . $node->getName() . "\r\n";
if(property_exists($node,"data"))
foreach ($node->data as $d){
if(is_array($d))
{
foreach ($d as $c)
{
//$txtstr .= $node->export($c,$level + 1);
$p = "";
$params = @$c->getParameters();
if(count($params) > 0)
{
foreach($params as $key => $value){
$p .= ";" . strtoupper($key) . "=" . $value;
}
}
$txtstr .= $this->printDataLine($c, $p);
}
}
else
{
$p = "";
$params = @$d->getParameters();
if(count($params) > 0)
{
foreach($params as $key => $value){
$p .= ";" . strtoupper($key) . "=" . $value;
}
}
$txtstr .= $this->printDataLine($d, $p);
/*
$values = $d->getValues();
// don't think we need this, Sunbird does not like it in the EXDATE field
//$values = str_replace(",", "\\,", $values);
$line = $d->getName() . $p . ":" . $values;
$line = str_replace(array("<br>","<BR>","<br/>","<BR/"),"\\n",$line);
$line = str_replace(array("\r\n","\n\r","\n","\r"),'\n',$line);
//$line = str_replace(array(',',';','\\'), array('\\,','\\;','\\\\'),$line);
//$line =strip_tags($line);
$linecount = 0;
while (strlen($line) > 0) {
$linewidth = ($linecount == 0? 75 : 74);
$linesize = (strlen($line) > $linewidth? $linewidth: strlen($line));
if($linecount > 0)
$txtstr .= " ";
$txtstr .= substr($line,0,$linesize) . "\r\n";
$linecount += 1;
$line = substr($line,$linewidth);
}
*/
}
//echo $line . "\n";
}
if(property_exists($node,"child"))
foreach ($node->child as $c){
$txtstr .= $node->export($c,$level + 1);
}
$txtstr .= "END:" . $node->getName() . "\r\n";
return $txtstr;
}
/**
* print an attribute line
* @param object $d attributes
* @param object $p properties
*
*/
function printDataLine($d, $p)
{
$txtstr = "";
$values = $d->getValues();
// don't think we need this, Sunbird does not like it in the EXDATE field
//$values = str_replace(",", "\\,", $values);
$line = $d->getName() . $p . ":" . $values;
$line = str_replace(array("<br>","<BR>","<br/>","<BR/"),"\\n",$line);
$line = str_replace(array("\r\n","\n\r","\n","\r"),'\n',$line);
//$line = str_replace(array(',',';','\\'), array('\\,','\\;','\\\\'),$line);
//$line =strip_tags($line);
$linecount = 0;
while (strlen($line) > 0) {
$linewidth = ($linecount == 0? 75 : 74);
$linesize = (strlen($line) > $linewidth? $linewidth: strlen($line));
if($linecount > 0)
$txtstr .= " ";
$txtstr .= substr($line,0,$linesize) . "\r\n";
$linecount += 1;
$line = substr($line,$linewidth);
}
return $txtstr;
}
}
/**
*
* The main iCalendar object containing ZCiCalDataNodes and ZCiCalNodes.
*
*/
class ZCiCal {
/**
* The root node of the object tree
*
* @var object
*/
var $tree=null;
/**
* The most recently created node in the tree
*
* @var object
*/
var $curnode=null;
/**
* The main iCalendar object containing ZCiCalDataNodes and ZCiCalNodes.
*
* use maxevents and startevent to read events in multiple passes (to save memory)
*
* @param string $data icalendar feed string (empty if creating new feed)
*
* @param int $maxevents maximum # of events to read
*
* @param int $startevent starting event to read
*
* @return void
*
*
*/
function __construct($data = "", $maxevents = 1000000, $startevent = 0) {
if($data != ""){
// unfold lines
// first change all eol chars to "\n"
$data = str_replace(array("\r\n", "\n\r", "\n", "\r"), "\n", $data);
// now unfold lines
//$data = str_replace(array("\n ", "\n "),"!?", $data);
$data = str_replace(array("\n ", "\n "),"", $data);
// replace special iCal chars
$data = str_replace(array("\\\\","\,"),array("\\",","), $data);
// parse each line
$lines = explode("\n", $data);
$linecount = 0;
$eventcount = 0;
$eventpos = 0;
foreach($lines as $line) {
//$line = str_replace("!?", "\n", $line); // add nl back into descriptions
// echo ($linecount + 1) . ": " . $line . "<br/>";
if(substr($line,0,6) == "BEGIN:") {
// start new object
$name = substr($line,6);
if($name == "VEVENT")
{
if($eventcount < $maxevents && $eventpos >= $startevent)
{
$this->curnode = new ZCiCalNode($name, $this->curnode);
if($this->tree == null)
$this->tree = $this->curnode;
}
}
else
{
$this->curnode = new ZCiCalNode($name, $this->curnode);
if($this->tree == null)
$this->tree = $this->curnode;
}
//echo "new node: " . $this->curnode->name . "<br/>\n";
/*
if($this->curnode->getParent() != null)
echo "parent of " . $this->curnode->getName() . " is " . $this->curnode->getParent()->getName() . "<br/>";
else
echo "parent of " . $this->curnode->getName() . " is null<br/>";
*/
}
else if(substr($line,0,4) == "END:") {
$name = substr($line,4);
if($name == "VEVENT")
{
if($eventcount < $maxevents && $eventpos >= $startevent)
{
$eventcount++;
if($this->curnode->getName() != $name) {
//panic, mismatch in iCal structure
//die("Can't read iCal file structure, expecting " . $this->curnode->getName() . " but reading $name instead");
throw new Exception("Can't read iCal file structure, expecting " . $this->curnode->getName() . " but reading $name instead");
}
if($this->curnode->getParent() != null) {
//echo "moving up from " . $this->curnode->getName() ;
$this->curnode = & $this->curnode->getParent();
//echo " to " . $this->curnode->getName() . "<br/>";
//echo $this->curnode->getName() . " has " . count($this->curnode->child) . " children<br/>";
}
}
$eventpos++;
}
else
{
if($this->curnode->getName() != $name) {
//panic, mismatch in iCal structure
//die("Can't read iCal file structure, expecting " . $this->curnode->getName() . " but reading $name instead");
throw new Exception("Can't read iCal file structure, expecting " . $this->curnode->getName() . " but reading $name instead");
}
if($this->curnode->getParent() != null) {
//echo "moving up from " . $this->curnode->getName() ;
$this->curnode = & $this->curnode->getParent();
//echo " to " . $this->curnode->getName() . "<br/>";
//echo $this->curnode->getName() . " has " . count($this->curnode->child) . " children<br/>";
}
}
}
else {
$datanode = new ZCiCalDataNode($line);
if($this->curnode->getName() == "VEVENT")
{
if($eventcount < $maxevents && $eventpos >= $startevent)
{
if($datanode->getName() == "EXDATE")
{
if(!array_key_exists($datanode->getName(),$this->curnode->data))
{
$this->curnode->data[$datanode->getName()] = $datanode;
}
else
{
$this->curnode->data[$datanode->getName()]->value[] = $datanode->value[0];
}
}
else
{
if(!array_key_exists($datanode->getName(),$this->curnode->data))
{
$this->curnode->data[$datanode->getName()] = $datanode;
}
else
{
$tnode = $this->curnode->data[$datanode->getName()];
$this->curnode->data[$datanode->getName()] = array();
$this->curnode->data[$datanode->getName()][] = $tnode;
$this->curnode->data[$datanode->getName()][] = $datanode;
}
}
}
}
else
{
if($datanode->getName() == "EXDATE")
{
if(!array_key_exists($datanode->getName(),$this->curnode->data))
{
$this->curnode->data[$datanode->getName()] = $datanode;
}
else
{
$this->curnode->data[$datanode->getName()]->value[] = $datanode->value[0];
}
}
else
{
if(!array_key_exists($datanode->getName(),$this->curnode->data))
{
$this->curnode->data[$datanode->getName()] = $datanode;
}
else
{
$tnode = $this->curnode->data[$datanode->getName()];
$this->curnode->data[$datanode->getName()] = array();
$this->curnode->data[$datanode->getName()][] = $tnode;
$this->curnode->data[$datanode->getName()][] = $datanode;
}
}
}
}
$linecount++;
}
}
else {
$name = "VCALENDAR";
$this->curnode = new ZCiCalNode($name, $this->curnode);
$this->tree = $this->curnode;
$datanode = new ZCiCalDataNode("VERSION:2.0");
$this->curnode->data[$datanode->getName()] = $datanode;
$datanode = new ZCiCalDataNode("PRODID:-//ZContent.net//ZapCalLib 1.0//EN");
$this->curnode->data[$datanode->getName()] = $datanode;
$datanode = new ZCiCalDataNode("CALSCALE:GREGORIAN");
$this->curnode->data[$datanode->getName()] = $datanode;
$datanode = new ZCiCalDataNode("METHOD:PUBLISH");
$this->curnode->data[$datanode->getName()] = $datanode;
}
}
/**
* CountEvents()
*
* Return the # of VEVENTs in the object
*
* @return int
*/
function countEvents() {
$count = 0;
if(isset($this->tree->child)){
foreach($this->tree->child as $child){
if($child->getName() == "VEVENT")
$count++;
}
}
return $count;
}
/**
* CountVenues()
*
* Return the # of VVENUEs in the object
*
* @return int
*/
function countVenues() {
$count = 0;
if(isset($this->tree->child)){
foreach($this->tree->child as $child){
if($child->getName() == "VVENUE")
$count++;
}
}
return $count;
}
/**
* Export object to string
*
* This function exports all objects to an iCalendar string
*
* @return string an iCalendar formatted string
*/
function export() {
return $this->tree->export($this->tree);
}
/**
* Get first event in object list
* Use getNextEvent() to navigate through list
*
* @return object The first event, or null
*/
function &getFirstEvent() {
static $nullguard = null;
if ($this->countEvents() > 0){
$child = $this->tree->child[0];
$event=false;
while(!$event && $child != null){
if($child->getName() == "VEVENT")
$event = true;
else
$child = $child->next;
}
return $child;
}
else
return $nullguard;
}
/**
* Get next event in object list
*
* @param object $event The current event object
*
* @return object Returns the next event or null if past last event
*/
function &getNextEvent($event){
do{
$event = $event->next;
} while($event != null && $event->getName() != "VEVENT");
return $event;
}
/**
* Get first venue in object list
* Use getNextVenue() to navigate through list
*
* @return object The first venue, or null
*/
function &getFirstVenue() {
static $nullguard = null;
if ($this->countVenues() > 0){
$child = $this->tree->child[0];
$event=false;
while(!$event && $child != null){
if($child->getName() == "VVENUE")
$event = true;
else
$child = $child->next;
}
return $child;
}
else
return $nullguard;
}
/**
* Get next venue in object list
*
* @param object $venue The current venue object
*
* @return object Returns the next venue or null if past last venue
*/
function &getNextVenue($venue){
do{
$venue = $venue->next;
} while($venue != null && $venue->getName() != "VVENUE");
return $venue;
}
/**
* Get first child in object list
* Use getNextSibling() and getPreviousSibling() to navigate through list
*
* @param object $thisnode The parent object
*
* @return object The child object
*/
function &getFirstChild(& $thisnode){
$nullvalue = null;
if(count($thisnode->child) > 0) {
//echo "moving from " . $thisnode->getName() . " to " . $thisnode->child[0]->getName() . "<br/>";
return $thisnode->child[0];
}
else
return $nullvalue;
}
/**
* Get next sibling in object list
*
* @param object $thisnode The current object
*
* @return object Returns the next sibling
*/
function &getNextSibling(& $thisnode){
return $thisnode->next;
}
/**
* Get previous sibling in object list
*
* @param object $thisnode The current object
*
* @return object Returns the previous sibling
*/
function &getPrevSibling(& $thisnode){
return $thisnode->prev;
}
/**
* Read date/time in iCal formatted string
*
* @param string iCal formated date/time string
*
* @return int Unix timestamp
* @deprecated Use ZDateHelper::toUnixDateTime() instead
*/
function toUnixDateTime($datetime){
$year = substr($datetime,0,4);
$month = substr($datetime,4,2);
$day = substr($datetime,6,2);
$hour = 0;
$minute = 0;
$second = 0;
if(strlen($datetime) > 8 && $datetime[8] == "T") {
$hour = substr($datetime,9,2);
$minute = substr($datetime,11,2);
$second = substr($datetime,13,2);
}
$d1 = mktime($hour, $minute, $second, $month, $day, $year);
}
/**
* fromUnixDateTime()
*
* Take Unix timestamp and format to iCal date/time string
*
* @param int $datetime Unix timestamp, leave blank for current date/time
*
* @return string formatted iCal date/time string
* @deprecated Use ZDateHelper::fromUnixDateTimetoiCal() instead
*/
static function fromUnixDateTime($datetime = null){
date_default_timezone_set('UTC');
if($datetime == null)
$datetime = time();
return date("Ymd\THis",$datetime);
}
/**
* fromUnixDate()
*
* Take Unix timestamp and format to iCal date string
*
* @param int $datetime Unix timestamp, leave blank for current date/time
*
* @return string formatted iCal date string
* @deprecated Use ZDateHelper::fromUnixDateTimetoiCal() instead
*/
static function fromUnixDate($datetime = null){
date_default_timezone_set('UTC');
if($datetime == null)
$datetime = time();
return date("Ymd",$datetime);
}
/**
* Format into iCal time format from SQL date or SQL date-time format
*
* @param string $datetime SQL date or SQL date-time string
*
* @return string iCal formatted string
* @deprecated Use ZDateHelper::fromSqlDateTime() instead
*/
static function fromSqlDateTime($datetime = ""){
if($datetime == "")
$datetime = ZDateHelper::toSqlDateTime();
if(strlen($datetime) > 10)
return sprintf('%04d%02d%02dT%02d%02d%02d',substr($datetime,0,4),substr($datetime,5,2),substr($datetime,8,2),
substr($datetime,11,2),substr($datetime,14,2),substr($datetime,17,2));
else
return sprintf('%04d%02d%02d',substr($datetime,0,4),substr($datetime,5,2),substr($datetime,8,2));
}
/**
* Format iCal time format to either SQL date or SQL date-time format
*
* @param string $datetime icalendar formatted date or date-time
* @return string SQL date or SQL date-time string
* @deprecated Use ZDateHelper::toSqlDateTime() instead
*/
static function toSqlDateTime($datetime = ""){
if($datetime == "")
return ZDateHelper::toSqlDateTime();
if(strlen($datetime) > 10)
return sprintf('%04d-%02d-%02d %02d:%02d:%02d',substr($datetime,0,4),substr($datetime,5,2),substr($datetime,8,2),
substr($datetime,11,2),substr($datetime,14,2),substr($datetime,17,2));
else
return sprintf('%04d-%02d-%02d',substr($datetime,0,4),substr($datetime,5,2),substr($datetime,8,2));
}
/**
* Pull timezone data from node and put in array
*
* Returning array contains the following array keys: tzoffsetfrom, tzoffsetto, tzname, dtstart, rrule
*
* @param array $node timezone object
*
* @return array
*/
static function getTZValues($node){
$tzvalues = array();
$tnode = @$node->data['TZOFFSETFROM'];
if($tnode != null){
$tzvalues["tzoffsetfrom"] = $tnode->getValues();
}
$tnode = @$node->data['TZOFFSETTO'];
if($tnode != null){
$tzvalues["tzoffsetto"] = $tnode->getValues();
}
$tnode = @$node->data['TZNAME'];
if($tnode != null){
$tzvalues["tzname"] = $tnode->getValues();
}
else
$tzvalues["tzname"] = "";
$tnode = @$node->data['DTSTART'];
if($tnode != null){
$tzvalues["dtstart"] = ZDateHelper::fromiCaltoUnixDateTime($tnode->getValues());
}
$tnode = @$node->data['RRULE'];
if($tnode != null){
$tzvalues["rrule"] = $tnode->getValues();
//echo "rule: " . $tzvalues["rrule"] . "<br/>\n";
}
else{
// no rule specified, let's create one from based on the date
$date = getdate($tzvalues["dtstart"]);
$month = $date["mon"];
$day = $date["mday"];
$tzvalues["rrule"] = "FREQ=YEARLY;INTERVAL=1;BYMONTH=$month;BYMONTHDAY=$day";
}
return $tzvalues;
}
/**
* Escape slashes, commas and semicolons in strings
*
* @param string $content
*
* @return string
*/
static function formatContent($content)
{
$content = str_replace(array('\\' , ',' , ';' ), array('\\\\' , '\\,' , '\\;' ),$content);
return $content;
}
}
?>

View File

@@ -0,0 +1,6 @@
<html>
<head>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,796 @@
<?php
/**
* recurringdate.php - create list of dates from recurring rule
*
* @package ZapCalLib
* @author Dan Cogliano <http://zcontent.net>
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
* @link http://icalendar.org/php-library.html
*/
// No direct access
defined('_ZAPCAL') or die( 'Restricted access' );
/**
* Zap Calendar Recurring Date Helper Class
*
* Class to expand recurring rule to a list of dates
*/
class ZCRecurringDate {
/**
* rules string
*
* @var string
*/
var $rules = "";
/**
* start date in Unix Timestamp format (local timezone)
*
* @var integer
*/
var $startdate = null;
/**
* repeating frequency type (i.e. "y" for yearly, "m" for monthly)
*
* @var string
*/
var $freq = null;
/**
* timezone of event (using PHP timezones)
*
* @var string
*/
var $tzid = null;
/**
* repeat mode ('c': count, 'u': until)
*
* @var string
*/
var $repeatmode=null;
/**
* repeat until date (in UTC Unix Timestamp format)
*
* @var integer
*/
var $until=null;
/**
* repeat count when repeat mode is 'c'
*
* @var integer
*/
var $count=0;
/**
* array of repeat by seconds values
*
* @var array
*/
var $bysecond=array();
/**
* array of repeat by minutes values
*
* @var array
*/
var $byminute=array();
/**
* array of repeat by hour values
*
* @var array
*/
var $byhour=array();
/**
* array of repeat by day values
*
* @var array
*/
var $byday=array();
/**
* array of repeat by month day values
*
* @var array
*/
var $bymonthday=array();
/**
* array of repeat by month values
*
* @var array
*/
var $bymonth=array();
/**
* array of repeat by year values
*
* @var array
*/
var $byyear=array();
/**
* array of repeat by setpos values
*
* @var array
*/
var $bysetpos=array();
/**
* inteval of repeating event (i.e. every 2 weeks, every 6 months)
*
* @var integer
*/
var $interval = 1;
/**
* debug level (for testing only)
*
* @var integer
*/
var $debug = 0;
/**
* error string (future use)
*
* @var string
*/
var $error;
/**
* array of exception dates in Unix Timestamp format (UTC dates)
*
* @var array
*/
var $exdates=array();
/**
* Expand recurring rule to a list of dates
*
* @param string $rules iCalendar rules string
* @param integer $startdate start date in Unix Timestamp format
* @param array $exdates array of exception dates
* @param string $tzid timezone of event (using PHP timezones)
*/
function __construct($rules, $startdate, $exdates = array(),$tzid = "UTC"){
if(strlen($rules) > 0){
//move exdates to event timezone for comparing with event date
for($i = 0; $i < count($exdates); $i++)
{
$exdates[$i] = ZDateHelper::toUnixDateTime(ZDateHelper::toLocalDateTime(ZDateHelper::toSQLDateTime($exdates[$i]),$tzid));
}
$rules=str_replace("\'","",$rules);
$this->rules = $rules;
if($startdate == null){
// if not specified, use start date of beginning of last year
$tdate=getdate();
$startdate=mktime(0,0,0,1,1,$tdate["year"] - 1);
}
$this->startdate = $startdate;
$this->tzid = $tzid;
$this->exdates = $exdates;
$rules=explode(";", $rules);
$ruletype = "";
foreach($rules as $rule){
$item=explode("=",$rule);
//echo $item[0] . "=" . $item[1] . "<br/>\n";
switch($item[0]){
case "FREQ":
switch($item[1]){
case "YEARLY":
$this->freq="y";
break;
case "MONTHLY":
$this->freq="m";
break;
case "WEEKLY":
$this->freq="w";
break;
case "DAILY":
$this->freq="d";
break;
case "HOURLY":
$this->freq="h";
break;
case "MINUTELY":
$this->freq="i";
break;
case "SECONDLY":
$this->freq="s";
break;
}
break;
case "INTERVAL":
$this->interval = $item[1];
break;
case "BYSECOND":
$this->bysecond = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "BYMINUTE":
$this->byminute = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "BYHOUR":
$this->byhour = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "BYDAY":
$this->byday = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "BYMONTHDAY":
$this->bymonthday = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "BYMONTH":
$this->bymonth = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "BYYEAR":
$this->byyear = explode(",",$item[1]);
$ruletype = $item[0];
break;
case "COUNT":
$this->count = intval($item[1]);
$this->repeatmode = "c";
break;
case "BYSETPOS":
$this->bysetpos = explode(",",$item[1]);
break;
case "UNTIL":
$this->until = ZDateHelper::fromiCaltoUnixDateTime($item[1]);
$this->repeatmode = "u";
break;
}
}
if(count($this->bysetpos) > 0){
switch($ruletype){
case "BYYEAR":
$this->byyear = $this->bySetPos($this->byyear,$this->bysetpos);
break;
case "BYMONTH":
$this->bymonth = $this->bySetPos($this->bymonth,$this->bysetpos);
break;
case "BYMONTHDAY":
$this->bymonthday = $this->bySetPos($this->bymonthday,$this->bysetpos);
break;
case "BYDAY":
$this->byday = $this->bySetPos($this->byday,$this->bysetpos);
break;
case "BYHOUR":
$this->byhour = $this->bySetPos($this->byhour,$this->bysetpos);
break;
case "BYMINUTE":
$this->byminute = $this->bySetPos($this->byminute,$this->bysetpos);
break;
case "BYSECOND":
$this->bysecond = $this->bySetPos($this->bysecond,$this->bysetpos);
break;
}
}
}
}
/**
* bysetpos rule support
*
* @param array $bytype
* @param array $bysetpos
*
* @return array
*/
function bySetPos($bytype, $bysetpos){
$result = array();
for($i=0; $i < count($bysetpos); $i++){
for($j=0; $j < count($bytype); $j++){
$result[] = $bysetpos[$i] . $bytype[$j];
}
}
return $result;
}
/**
* save error
*
* @param string $msg
*/
function setError($msg){
$this->error = $msg;
}
/**
* get error message
*
* @return string error message
*/
function getError(){
return $this->error;
}
/**
* set debug level (0: none, 1: minimal, 2: more output)
*
* @param integer $level
*
*/
function setDebug($level)
{
$this->debug = $level;
}
/**
* display debug message
*
* @param integer $level
* @param string $msg
*/
function debug($level, $msg){
if($this->debug >= $level)
echo $msg . "<br/>\n";
}
/**
* Get repeating dates by year
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function byYear($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"byYear(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$count = 0;
if(count($this->byyear) > 0){
foreach($this->byyear as $year){
$t = getdate($startdate);
$wdate = mktime($t[hours],$t[minutes],$t[seconds],$t[month],$t[mday],$year);
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->byMonth($wdate, $enddate, $rdates, $tzid);
if($count == 0) {
$rdates[] = $wdate;
$count++;
}
}
}
}
else if(!$this->maxDates($rdates))
$count = $this->byMonth($startdate, $enddate, $rdates, $tzid);
self::debug(1,"byYear() returned " . $count );
return $count;
}
/**
* Get repeating dates by month
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function byMonth($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"byMonth(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$count = 0;
if(count($this->bymonth) > 0){
foreach($this->bymonth as $month){
$t = getdate($startdate);
$wdate = mktime($t["hours"],$t["minutes"],$t["seconds"],$month,$t["mday"],$t["year"]);
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->byMonthDay($wdate, $enddate, $rdates, $tzid);
if($count == 0) {
$rdates[] = $wdate;
$count++;
}
}
}
}
else if(!$this->maxDates($rdates))
$count = $this->byMonthDay($startdate, $enddate, $rdates, $tzid);
self::debug(1,"byMonth() returned " . $count );
return $count;
}
/**
* Get repeating dates by month day
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function byMonthDay($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"byMonthDay(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$count = 0;
self::debug(1,"start date: " . ZDateHelper::toSqlDateTime($startdate));
if(count($this->bymonthday) > 0){
foreach($this->bymonthday as $day){
$day = intval($day);
$t = getdate($startdate);
$wdate = mktime($t['hours'],$t['minutes'],$t['seconds'],$t['mon'],$day,$t['year']);
self::debug(2,"mktime(" . $t['hours'] . ", " . $t['minutes']
. ", " . $t['mon'] . ", " . $day . ", " . $t['year'] . ") returned $wdate");
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->byDay($wdate, $enddate, $rdates, $tzid);
if($count == 0) {
$rdates[] = $wdate;
$count++;
}
}
}
}
else if(!$this->maxDates($rdates)) {
self::debug(1,"start date: " . ZDateHelper::toSqlDateTime($startdate));
$count = $this->byDay($startdate, $enddate, $rdates, $tzid);
}
self::debug(1,"byMonthDay() returned " . $count );
return $count;
}
/**
* Get repeating dates by day
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function byDay($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"byDay(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$days = array(
"SU" => 0,
"MO" => 1,
"TU" => 2,
"WE" => 3,
"TH" => 4,
"FR" => 5,
"SA" => 6);
$idays = array(
0 => "SU",
1 => "MO",
2 => "TU",
3 => "WE",
4 => "TH",
5 => "FR",
6 => "SA");
$count = 0;
if(count($this->byday) > 0){
if(empty($this->byday[0]))
{
$this->byday[0] = $idays[date("w",$startdate)];
}
foreach($this->byday as $tday){
$t = getdate($startdate);
$day = substr($tday,strlen($tday) - 2);
if(strlen($day) < 2)
{
// missing start day, use current date for DOW
$day = $idays[date("w",$startdate)];
}
if(strlen($tday) > 2) {
$imin = 1;
$imax = 5; // max # of occurances in a month
if(strlen($tday) > 2)
$imin = $imax = substr($tday,0,strlen($tday) - 2);
self::debug(2,"imin: $imin, imax: $imax, tday: $tday, day: $day, daynum: {$days[$day]}");
for($i = $imin; $i <= $imax; $i++){
$wdate = ZDateHelper::getDateFromDay($startdate,$i-1,$days[$day],$tzid);
self::debug(2,"getDateFromDay(" . ZDateHelper::toSqlDateTime($startdate)
. ",$i,{$days[$day]}) returned " . ZDateHelper::toSqlDateTime($wdate));
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->byHour($wdate, $enddate, $rdates);
if($count == 0){
$rdates[] = $wdate;
$count++;
//break;
}
}
}
}
else {
// day of week version
$startdate_dow = date("w",$startdate);
$datedelta = $days[$day] - $startdate_dow;
self::debug(2, "start_dow: $startdate_dow, datedelta: $datedelta");
if($datedelta >= 0)
{
$wdate = ZDateHelper::addDate($startdate,0,0,0,0,$datedelta,0,$this->tzid);
self::debug(2, "wdate: " . ZDateHelper::toSqlDateTime($wdate));
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->byHour($wdate, $enddate, $rdates);
if($count == 0){
$rdates[] = $wdate;
$count++;
self::debug(2,"adding date " . ZDateHelper::toSqlDateTime($wdate) );
}
}
}
}
}
}
else if(!$this->maxDates($rdates))
$count = $this->byHour($startdate, $enddate, $rdates);
self::debug(1,"byDay() returned " . $count );
return $count;
}
/**
* Get repeating dates by hour
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function byHour($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"byHour(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$count = 0;
if(count($this->byhour) > 0){
foreach($this->byhour as $hour){
$t = getdate($startdate);
$wdate = mktime($hour,$t["minutes"],$t["seconds"],$t["mon"],$t["mday"],$t["year"]);
self::debug(2,"checking date/time " . ZDateHelper::toSqlDateTime($wdate));
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->byMinute($wdate, $enddate, $rdates);
if($count == 0) {
$rdates[] = $wdate;
$count++;
}
}
}
}
else if(!$this->maxDates($rdates))
$count = $this->byMinute($startdate, $enddate, $rdates);
self::debug(1,"byHour() returned " . $count );
return $count;
}
/**
* Get repeating dates by minute
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function byMinute($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"byMinute(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$count = 0;
if(count($this->byminute) > 0){
foreach($this->byminute as $minute){
$t = getdate($startdate);
$wdate = mktime($t["hours"],$minute,$t["seconds"],$t["mon"],$t["mday"],$t["year"]);
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$count = $this->bySecond($wdate, $enddate, $rdates);
if($count == 0) {
$rdates[] = $wdate;
$count++;
}
}
}
}
else if(!$this->maxDates($rdates))
$count = $this->bySecond($startdate, $enddate, $rdates);
self::debug(1,"byMinute() returned " . $count );
return $count;
}
/**
* Get repeating dates by second
*
* @param integer $startdate start date of repeating events, in Unix timestamp format
* @param integer $enddate end date of repeating events, in Unix timestamp format
* @param array $rdates array to contain expanded repeating dates
* @param string $tzid timezone of event (using PHP timezones)
*
* @return integer count of dates
*/
private function bySecond($startdate, $enddate, &$rdates, $tzid="UTC"){
self::debug(1,"bySecond(" . ZDateHelper::toSqlDateTime($startdate) . ","
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
$count = 0;
if(count($this->bysecond) > 0){
foreach($this->bysecond as $second){
$t = getdate($startdate);
$wdate = mktime($t["hours"],$t["minutes"],$second,$t["mon"],$t["mday"],$t["year"]);
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
$rdates[] = $wdate;
$count++;
}
}
}
self::debug(1,"bySecond() returned " . $count );
return $count;
}
/**
* Determine if the loop has reached the end date
*
* @param array $rdates array of repeating dates
*
* @return boolean
*/
private function maxDates($rdates){
if($this->repeatmode == "c" && count($rdates) >= $this->count)
return true; // exceeded count
else if(count($rdates) > 0 && $this->repeatmode == "u" && $rdates[count($rdates) - 1] > $this->until){
return true; //past date
}
return false;
}
/**
* Get array of dates from recurring rule
*
* @param $maxdate integer maximum date to appear in repeating dates in Unix timestamp format
*
* @return array
*/
public function getDates($maxdate = null){
//$this->debug = 2;
self::debug(1,"getDates()");
$nextdate = $enddate = $this->startdate;
$rdates = array();
$done = false;
$eventcount = 0;
$loopcount = 0;
self::debug(2,"freq: " . $this->freq . ", interval: " . $this->interval);
while(!$done){
self::debug(1,"<b>*** Frequency ({$this->freq}) loop pass $loopcount ***</b>");
switch($this->freq){
case "y":
if($eventcount > 0)
{
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,0,$this->interval,$this->tzid);
self::debug(2,"addDate() returned " . ZDateHelper::toSqlDateTime($nextdate));
if(!empty($this->byday)){
$t = getdate($nextdate);
$nextdate = gmmktime($t["hours"],$t["minutes"],$t["seconds"],$t["mon"],1,$t["year"]);
}
self::debug(2,"nextdate set to $nextdate (". ZDateHelper::toSQLDateTime($nextdate) . ")");
}
$enddate=ZDateHelper::addDate($nextdate,0,0,0,0,0,1);
break;
case "m":
if($eventcount > 0)
{
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,$this->interval,0,0,$this->tzid);
self::debug(2,"addDate() returned " . ZDateHelper::toSqlDateTime($nextdate));
}
if(count($this->byday) > 0)
{
$t = getdate($nextdate);
if($t["mday"] > 28)
{
//check for short months when using month by day, make sure we do not overshoot the counter and skip a month
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,$this->interval,0,0,$this->tzid);
$t2 = getdate($nextdate);
if($t2["mday"] < $t["mday"])
{
// oops, skipped a month, backup to previous month
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,$t2["mday"] - $t["mday"],0,$this->tzid);
}
}
$t = getdate($nextdate);
$nextdate = mktime($t["hours"],$t["minutes"],$t["seconds"],$t["mon"],1,$t["year"]);
}
self::debug(2,"nextdate set to $nextdate (". ZDateHelper::toSQLDateTime($nextdate) . ")");
$enddate=ZDateHelper::addDate($nextdate,0,0,0,$this->interval,0,0);
break;
case "w":
if($eventcount == 0)
$nextdate=$nextdate;
else {
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,$this->interval*7,0,$this->tzid);
if(count($this->byday) > 0){
$dow = date("w", $nextdate);
// move to beginning of week (Sunday)
$bow = 0;
$diff = $bow - $dow;
if($diff > 0)
$diff = $diff - 7;
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,$diff,0);
}
self::debug(2,"nextdate set to $nextdate (". ZDateHelper::toSQLDateTime($nextdate) . ")");
}
$enddate=ZDateHelper::addDate($nextdate,0,0,0,0,$this->interval*7,0);
break;
case "d":
$nextdate=($eventcount==0?$nextdate:
ZDateHelper::addDate($nextdate,0,0,0,0,$this->interval,0,$this->tzid));
$enddate=ZDateHelper::addDate($nextdate,0,0,0,0,1,0);
break;
}
$count = $this->byYear($nextdate,$enddate,$rdates,$this->tzid);
$eventcount += $count;
if($maxdate > 0 && $maxdate < $nextdate)
{
array_pop($rdates);
$done = true;
}
else if($count == 0 && !$this->maxDates($rdates)){
$rdates[] = $nextdate;
$eventcount++;
}
if($this->maxDates($rdates))
$done = true;
$year = date("Y", $nextdate);
if($year > _ZAPCAL_MAXYEAR)
{
$done = true;
}
$loopcount++;
if($loopcount > _ZAPCAL_MAXYEAR){
$done = true;
throw new Exception("Infinite loop detected in getDates()");
}
}
if($this->repeatmode == "u" && $rdates[count($rdates) - 1] > $this->until){
// erase last item
array_pop($rdates);
}
$count1 = count($rdates);
$rdates = array_unique($rdates);
$count2 = count($rdates);
$dups = $count1 - $count2;
$excount = 0;
foreach($this->exdates as $exdate)
{
if($pos = array_search($exdate,$rdates))
{
array_splice($rdates,$pos,1);
$excount++;
}
}
self::debug(1,"getDates() returned " . count($rdates) . " dates, removing $dups duplicates, $excount exceptions");
if($this->debug >= 2)
{
self::debug(2,"Recurring Dates:");
foreach($rdates as $rdate)
{
$d = getdate($rdate);
self::debug(2,ZDateHelper::toSQLDateTime($rdate) . " " . $d["wday"] );
}
self::debug(2,"Exception Dates:");
foreach($this->exdates as $exdate)
{
self::debug(2, ZDateHelper::toSQLDateTime($exdate));
}
//exit;
}
return $rdates;
}
}

View File

@@ -0,0 +1,142 @@
<?php
/**
* timezone.php - create timezone data for use in icalendar file
*
* @package ZapCalLib
* @author Dan Cogliano <http://zcontent.net>
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
* @link http://icalendar.org/php-library.html
*/
// No direct access
defined('_ZAPCAL') or die( 'Restricted access' );
/**
* Zap Calendar Time Zone Helper Class
*
* Class to help create timezone section of iCalendar file
*
* @copyright Copyright (C) 2006 - 2016 by Dan Cogliano
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
class ZCTimeZoneHelper {
/**
* getTZNode creates VTIMEZONE section in an iCalendar file
*
* @param @startyear int start year of date range
*
* @param @endyear int end year of date range
*
* @param $tzid string PHP timezone, use underscore for multiple words (i.e. "New_York" for "New York")
*
* @param $parentnode object iCalendar object where VTIMEZONE will be created
*
* @return object return VTIMEZONE object
*/
static function getTZNode($startyear, $endyear, $tzid, $parentnode)
{
$tzmins = array();
$tzmaxs = array();
if(!array_key_exists($tzid,$tzmins) || $tzmins[$tzid] > $startyear)
{
$tzmins[$tzid] = $startyear;
}
if(!array_key_exists($tzid,$tzmaxs) || $tzmaxs[$tzid] < $endyear)
{
$tzmaxs[$tzid] = $endyear;
}
foreach(array_keys($tzmins) as $tzid)
{
$tmin = $tzmins[$tzid] - 1;
if(array_key_exists($tzid,$tzmaxs))
{
$tmax = $tzmaxs[$tzid] + 1;
}
else
{
$tmax = $tzmins[$tzid] + 1;
}
$tstart = gmmktime(0,0,0,1,1,$tmin);
$tend = gmmktime(23,59,59,12,31,$tmax);
$tz = new DateTimeZone($tzid);
$transitions = $tz->getTransitions($tstart,$tend);
$tzobj = new ZCiCalNode("VTIMEZONE", $parentnode, true);
$datanode = new ZCiCalDataNode("TZID:" . str_replace("_"," ",$tzid));
$tzobj->data[$datanode->getName()] = $datanode;
$count = 0;
$lasttransition = null;
if(count($transitions) == 1)
{
// not enough transitions found, probably UTC
// lets add fake transition at end for those systems that need it (i.e. Outlook)
$t2 = array();
$t2["isdst"] = $transitions[0]["isdst"];
$t2["offset"] = $transitions[0]["offset"];
$t2["ts"] = $tstart;
$t2["abbr"] = $transitions[0]["abbr"];
$transitions[] = $t2;
}
foreach($transitions as $transition)
{
$count++;
if($count == 1)
{
$lasttransition = $transition;
continue; // skip first item
}
if($transition["isdst"] == 1)
{
$tobj = new ZCiCalNode("DAYLIGHT", $tzobj);
}
else
{
$tobj = new ZCiCalNode("STANDARD", $tzobj);
}
//$tzobj->data[$tobj->getName()] == $tobj;
// convert timestamp to local time zone
$ts = ZDateHelper::toUnixDateTime(ZDateHelper::toLocalDateTime(ZDateHelper::toSQLDateTime($transition["ts"]),$tzid));
$datanode = new ZCiCalDataNode("DTSTART:".ZDateHelper::toICalDateTime($ts));
$tobj->data[$datanode->getName()] = $datanode;
//echo $ts . " => " . ZDateHelper::toICalDateTime($ts) . "<br/>\n"; exit;
$toffset = $lasttransition["offset"];
$thours = intval($toffset/60/60);
$tmins = abs($toffset)/60 - intval(abs($toffset)/60/60)*60;
if($thours < 0)
{
$offset = sprintf("%03d%02d",$thours,$tmins);
}
else
{
$offset = sprintf("+%02d%02d",$thours,$tmins);
}
$datanode = new ZCiCalDataNode("TZOFFSETFROM:".$offset);
$tobj->data[$datanode->getName()] = $datanode;
$toffset = $transition["offset"];
$thours = intval($toffset/60/60);
$tmins = abs($toffset)/60 - intval(abs($toffset)/60/60)*60;
if($thours < 0)
{
$offset = sprintf("%03d%02d",$thours,$tmins);
}
else
{
$offset = sprintf("+%02d%02d",$thours,$tmins);
}
$datanode = new ZCiCalDataNode("TZOFFSETTO:".$offset);
$tobj->data[$datanode->getName()] = $datanode;
$datanode = new ZCiCalDataNode("TZNAME:".$transition["abbr"]);
$tobj->data[$datanode->getName()] = $datanode;
$lasttransition = $transition;
}
}
return $tzobj;
}
}