format($objDateTime::ATOM);
//http://stackoverflow.com/questions/21686539/regular-expression-for-full-iso-8601-date-syntax
//http://stackoverflow.com/questions/12756159/regex-and-iso8601-formated-datetime
function abort($message) {
$result->result->error = false;
$result->result->message = $message;
header('Content-Type: application/json');
echo json_encode($result);
die();
}
$iso8601Pattern = '/^\d{4}(-\d\d(-\d\d(T\d\d:\d\d(:\d\d)?(\.\d+)?(([+-]\d\d:\d\d)|Z)?)?)?)?$/i';
$singleYearPattern = '/^\d{4}$/';
//$iso8601Pattern = '/^(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:Z|[+-][01]\d:[0-5]\d)$/i';
if (isset($_REQUEST["default"]) & $_REQUEST["default"] != "") {
$testMatch = urldecode($_REQUEST["default"]);
if (!preg_match($iso8601Pattern,$testMatch) && $testMatch !== 'current'){
abort("The value for the default parameter is not a valid iso8601 dateTime string or has the value 'current'.");
}
$default = $testMatch;
}
if (isset($_REQUEST["operation"]) & $_REQUEST["operation"] != "") {
$testMatch = urldecode($_REQUEST["operation"]);
if (!$testMatch == 'snapToGrid' && !$testMatch == 'configureTimeline'){
echo 'Parameter operation is not valid (snapToGrid,configureTimeline).
';
die();
}
$operation = $testMatch;
}
if (isset($_REQUEST["userValue"]) & $_REQUEST["userValue"] != "") {
$testMatch = urldecode($_REQUEST["userValue"]);
if (!preg_match($iso8601Pattern,$testMatch)){
abort("The value for the userValue parameter is not a valid iso8601 dateTime string.");
}
$userValue = $testMatch;
}
if (isset($_REQUEST["extent"]) & $_REQUEST["extent"] != "") {
$testMatch = urldecode($_REQUEST["extent"]);
//search for comma
if (strpos($testMatch,',') !== false) {
//found single discrete values
$singleValues = explode(',',$testMatch);
//test format of each value
foreach ($singleValues as $dateTime) {
if (!preg_match($iso8601Pattern,$dateTime)){
abort("The value for the extent parameter is not csv list of a valid iso8601 dateTime strings.");
}
}
$kindOfExtent = "discreteValues";
} elseif (strpos($testMatch,'/') !== false) {
//found interval with duration
//extract values
if (count(explode('/',$testMatch)) !== 3 ) {
abort("The value for the extent parameter is not a valid iso8601 time duration string.");
} else {
$singleValues = explode('/',$testMatch);
//check the first 2 entries for iso8601 strings
for ($i=0; $i < 2; $i++) {
if (!preg_match($iso8601Pattern,$singleValues[$i])){
abort("The first two parts of the value for the extent parameter are not valid iso8601 dateTime strings.");
}
}
//check duration string
$iso8601DurationPattern = '/^P(?=\w*\d)(?:\d+Y|Y)?(?:\d+M|M)?(?:\d+W|W)?(?:\d+D|D)?(?:T(?:\d+H|H)?(?:\d+M|M)?(?:\d+(?:\.\d{1,2})?S|S)?)?$/';
if (!preg_match($iso8601DurationPattern,$singleValues[2])) {
abort("The third part of the value for the extent parameter is not a valid iso8601 duration string.");
}
}
$kindOfExtent = "intervalWithDuration";
} elseif (!preg_match($iso8601Pattern,$testMatch)) {
abort("The value for the extent parameter is not a valid iso8601 dateTime string.");
}
//everything is allright
$extent = $testMatch;
$testMatch = NULL;
}
//function to find the nearest interval of array elements to a given array value
//returns a value, if one special element is found or returns the smallest interval
//from the ordered array elements in which element lies in
function quicksearch($element, $searchArray) {
$numberOfValues = count($searchArray);
if ($numberOfValues == 2) {
return $searchArray;
}
while ($numberOfValues > 2) {
if ($element == $searchArray[ceil($numberOfValues / 2) - 1]) {
return $element; //return value is no array!
} else {
if ($element >= $searchArray[ceil($numberOfValues / 2) - 1]) {
$numberOfValues = count($searchArray);
if ($numberOfValues == 2) {
return $searchArrayNew;
}
$searchArrayNew = quicksearch($element, array_slice($searchArray, ceil($numberOfValues / 2) - 1));
} else {
$numberOfValues = count($searchArray);
if ($numberOfValues == 2) {
return $searchArray;
}
$searchArrayNew = quicksearch($element, array_slice($searchArray, 0, ceil($numberOfValues / 2)));
}
$numberOfValues = count($searchArrayNew);
if ($numberOfValues == 2) {
return $searchArrayNew;
}
}
}
return $searchArrayNew;
}
function getNearestValue($userValue, $discreteValues) {
$discreteDateTimes = array();
$numberOfValues = count($discreteValues);
$userValueDateTime = new DateTime($userValue);
//transform discrete strings to time
foreach ($discreteValues as $discreteValue) {
$discreteDateTimes[] = new DateTime($discreteValue);
}
//do quicksearch
//check if current value is less than first entry
if ($userValueDateTime <= $discreteDateTimes[0]) {
return ($discreteDateTimes[0]);
} else {
if ($userValueDateTime >= $discreteDateTimes[$numberOfValues-1]) {
return ($discreteDateTimes[$numberOfValues-1]);
} else {
//do a quicksearch in array
$result = quicksearch($userValueDateTime, $discreteDateTimes);
if (is_array($result) && count($result) == 2) {
//test the length of the interval
$diffTime1 = abs(interval2Seconds($result[0]->diff($userValueDateTime)));
$diffTime2 = abs(interval2Seconds($result[1]->diff($userValueDateTime)));
if ($diffTime1 > $diffTime2) {
return $result[1];
} else {
return $result[0];
}
} else {
//return value itself
return $result;
}
}
}
}
//function to convert a php datetime intervall to seconds
function interval2Seconds($duration) {
if ($duration->days == false) {
$seconds = $duration->s + $duration->i * 60 + $duration->h * 3600 + $duration->d * (3600*24) + $duration->m * (30.436875*3600*24) + $duration->y * (365*3600*24) ;
} else {
$seconds = $duration->s + $duration->i * 60 + $duration->h * 3600 + $duration->days * (3600*24);
}
return $seconds;
}
//Variable for result object
$result->data = array();
//define default timezone:
date_default_timezone_set('UTC');
//what do we need further for timeline:
$fullYearExtent = false;
//min, max, if point is slideable, if some point is already selected -> userValue
if ($operation == 'snapToGrid') {
//check for discrete values - if they exists - call a function to find next value - like quicksearch
if (strpos($extent,'/') === false) { //no intervall found in extent
$discreteValues = explode(',',$extent);
if (preg_match($singleYearPattern,$interval[0])) {
$fullYearExtent = true;
//$e = new mb_exception("single year pattern for snapping");
//set to middle of the year
//$interval[0] = $interval[0]."-07-02";
//$interval[1] = $interval[1]."-07-02";
}
//ordered??? - define discrete values to be ordered !!
$newValue = getNearestValue($userValue, $discreteValues);
//$e = new mb_exception("newtime: ".$newTime->format('c'));
if ($fullYearExtent == true) {
$result->data[0]->value = $newValue->format('Y');
} else {
$result->data[0]->value = $newValue->format('c');
}
$result->result->error = false;
$result->result->message = "All done";
} else {
$interval = explode('/',$extent);
$startTime = new DateTime($interval[0]);
//check full year extent for snapping
if (preg_match($singleYearPattern,$interval[0])) {
$fullYearExtent = true;
//$e = new mb_exception("single year pattern for snapping");
//set to middle of the year
$interval[0] = $interval[0]."-07-02";
//$interval[1] = $interval[1]."-07-02";
}
//$e = new mb_exception("default timezone: ".date_default_timezone_get());
$timezone = $startTime->getTimezone();
//timezone from starttime:
//test if timezone was found in starttime:
//$e = new mb_exception("timezone found in starttime: ".$timezone->getName());
//$e = new mb_exception("start timezone: ".$startTime->getTimezone());
$defaultTimeZone = timezone_open('UTC');
//$e = new mb_exception("timezone from starttime: ".date_default_timezone_get($timezone));
//problem - if no timezone is set use default GMT!!
//$endTime = new DateTime($interval[1]);
$duration = new DateInterval($interval[2]);
$userValueDateTime = new DateTime($userValue);
$diffTime = $startTime->diff($userValueDateTime); //creates php datetime interval object
$diffTimeSeconds = $diffTime->s + $diffTime->i * 60 + $diffTime->h * 3600 + $diffTime->days * (3600*24);
//$e = new mb_exception("days: ".$diffTime->days);
$seconds = $duration->s + $duration->i * 60 + $duration->h * 3600 + $duration->d * (3600*24) + $duration->m * (30.436875*3600*24) + $duration->y * (365*3600*24);
//$e = new mb_exception("durationdays: ".$duration->d);
$fullDiffInSteps = round($diffTimeSeconds / $seconds);
/*$e = new mb_exception("start: ".$interval[0]);
$e = new mb_exception("wish to select: ".$userValue);
$e = new mb_exception("steps: ".$fullDiffInSteps);
$e = new mb_exception("seconds: ".$seconds);*/
$dateIntervalNew = new DateInterval('PT'.$fullDiffInSteps * $seconds.'S');
$newTime = $startTime->add($dateIntervalNew);
//$newTime = $newTime->setTimezone($timezone);
$newTime = $newTime->setTimezone($defaultTimeZone);
//$e = new mb_exception("newtime: ".$newTime->format('c'));
if ($fullYearExtent == true) {
$result->data[0]->value = $newTime->format('Y');
} else {
$result->data[0]->value = $newTime->format('c');
}
$result->result->error = false;
$result->result->message = "All done";
}
} else {
switch ($kindOfExtent) {
case "intervalWithDuration":
//calculate discrete points
$interval = explode('/',$extent);
//check extent for single year entries
if (preg_match($singleYearPattern,$interval[0]) && preg_match($singleYearPattern,$interval[1])) {
$fullYearExtent = true;
//$e = new mb_exception("single year pattern");
//set to middle of the year
$interval[0] = $interval[0]."-07-02";
$interval[1] = $interval[1]."-07-02";
}
//parse one single year as date of the
//$e = new mb_exception("extent representation of starttime: ".$interval[0]);
$startTime = new DateTime($interval[0]);
//$e = new mb_exception("date representation of starttime: ".$startTime->format('c'));
$endTime = new DateTime($interval[1]);
$duration = new DateInterval($interval[2]);
$timezone = $startTime->getTimezone();
//$e = new mb_exception("timezone of starttime: ".$timezone->getName());
$diffTime = $startTime->diff($endTime); //creates php datetime interval object
$diffTimeSeconds = $diffTime->s + $diffTime->i * 60 + $diffTime->h * 3600 + $diffTime->days * (3600*24);
//Problem: DateInterval cannot be directly converted into seconds - only days are possible
//days are only given, if interval has more than some days - if not given, use d attribute
if ($duration->days == false) {
$seconds = $duration->s + $duration->i * 60 + $duration->h * 3600 + $duration->d * (3600*24) + $duration->m * (30.436875*3600*24) + $duration->y * (365*3600*24) ;
} else {
$seconds = $duration->s + $duration->i * 60 + $duration->h * 3600 + $duration->days * (3600*24);
}
$numberOfDiscreteValues = $diffTimeSeconds / $seconds;
if ($numberOfDiscreteValues > $maxEntries) {
//abort("Number of possible discrete values: ".$numberOfDiscreteValues." exeeds max allowed number for visualization of timeline: ".$maxEntries."!");
//use the default value or the userValue if given
if ($userValue !== false || $default !== false) {
if ($userValue !== false) {
$result->data[0]->id = 0;
$result->data[0]->content = $userValue;
$result->data[0]->start = $userValue;
} else {
$result->data[0]->id = 0;
$result->data[0]->content = $default;
if ($default == 'current') {
$result->data[0]->start = $endTime->format('c');
if ($fullYearExtent == true) {
$result->data[0]->content = $endTime->format('Y');
} else {
$result->data[0]->content = $endTime->format('c');
}
} else {
$result->data[0]->start = $default;
}
}
//set options to make a moving of value possible
$result->options->editable->updateTime = true;
//$result->options->configure = true;
//$result->options->timeAxis->scale = 'minute';
//$result->options->timeAxis->step = 5;
//better do snapping in callback function!
} else {
//set options to make a moving of value possible
$result->options->editable->updateTime = true;
}
} else {
//below max entries
//check for $maxConcurrentEntries;
$result->options->zoomMax = $seconds*$maxConcurrentEntries*1000;
$result->data[0]->id = 0;
if ($fullYearExtent == true) {
$result->data[0]->content = $startTime->format('Y');
} else {
$result->data[0]->content = $startTime->format('c');
}
$result->data[0]->start = $startTime->format('c');
for ($i=1; $i < $numberOfDiscreteValues+1; $i++) {
$time = $startTime->add($duration);
//$time->setTimezone($timezone);
$result->data[$i]->id = $i;
if ($fullYearExtent == true) {
$result->data[$i]->content = $time->format('Y');
$result->data[$i]->start = $time->format('c');
} else {
$result->data[$i]->content = $time->format('c');
$result->data[$i]->start = $time->format('c');
}
}
//$result->options->editable->updateTime = false;
//$result->options->editable->updateTime = false;
$result->options->editable = false;
}
//re-initialize starttime because in case of a interval the time is incremented in the else loop before
$oldStartTime = new DateTime($interval[0]);
$result->options->min = $oldStartTime->format('c');
$result->options->max = $endTime->format('c');
$result->result->error = false;
$result->result->message = "All done";
break;
case "discreteValues":
//$e = new mb_exception("discrete values");
if (count(explode(',',$extent)) > $maxEntries) {
$extentArray = explode(',',$extent);
//abort("Number of discrete values: ".count(explode(',',$extent))." exeeds max allowed number for visualization of timeline: ".$maxEntries."!");
//todo
//use the default value or the userValue if given
if ($userValue !== false || $default !== false) {
if ($userValue !== false) {
$result->data[0]->id = 0;
$result->data[0]->content = $userValue;
$result->data[0]->start = $userValue;
} else {
$result->data[0]->id = 0;
$result->data[0]->content = $default;
if ($default == 'current') {
$result->data[0]->start = $endTime->format('c');
if ($fullYearExtent == true) {
$result->data[0]->content = $endTime->format('Y');
} else {
$result->data[0]->content = $endTime->format('c');
}
} else {
$result->data[0]->start = $default;
}
}
//set options to make a moving of value possible
$result->options->editable->updateTime = true;
//$result->options->configure = true;
//$result->options->timeAxis->scale = 'minute';
//$result->options->timeAxis->step = 5;
//better do snapping in callback function!
} else {
//set options to make a moving of value possible
$result->options->editable->updateTime = true;
}
} else {
$extentArray = explode(',',$extent);
for ($i=0; $i < count($extentArray); $i++) {
$result->data[$i]->id = $i;
$result->data[$i]->content = $extentArray[$i];
$result->data[$i]->start = $extentArray[$i];
}
$result->options->editable = false;
}
//use first and last entry as borders
$result->options->min = $extentArray[0];
$result->options->max = $extentArray[count($extentArray) - 1];
//$result->options->editable = false;
$result->result->error = false;
$result->result->message = "All done";
break;
case "singleValue":
//push json values
echo $extent;
break;
}
}
header('Content-Type: application/json');
echo json_encode($result);
?>