DateTime with Timezone in php and Microsoft Graph API - php

I'm trying to read the calendar entries of rooms and output the next three events on their calendar. However I am testing first to see if I can get just the first 3 events of the day, but it seems the timezone or something else is causing it to not show events correctly.
This is a function I wrote:
function mb_get_meetings($mb_email = null, $mb_proxy = false, $mb_datetime_start = null, $mb_datetime_finish = null)
{
date_default_timezone_set('Australia/Melbourne');
// get the Microsoft Open Graph API
$mb_msgraph = json_decode(mb_microsoft_opengraph($mb_proxy), true); // custom function to get Beaker Token + $mb_proxy for internal proxy on or off for dev testing
$mb_msgraph_token = $mb_msgraph['access_token'];
$mb_datetimenow = new DateTime();
$mb_datetimezone = new DateTimeZone('Australia/Melbourne');
$mb_datetimenow->setTimezone($mb_datetimezone);
$mb_datetime_start = new DateTime($mb_datetime_start, $mb_datetimezone);
$mb_datetime_finish = new DateTime($mb_datetime_finish, $mb_datetimezone);
$mb_datetime_start = $mb_datetime_start->format('Y-m-d\TH:i:s.u');
$mb_datetime_finish = $mb_datetime_finish->format('Y-m-d\TH:i:s.u');
$mb_url_string = 'https://graph.microsoft.com/v1.0/users/' . $mb_email . '/calendar/calendarView?startDateTime=' . $mb_datetime_start . '&endDateTime=' . $mb_datetime_finish;
$mb_args = array(
'headers' => array(
'Authorization' => 'Bearer ' . $mb_msgraph_token,
'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8',
'Prefer' => 'outlook.timezone="Australia/Melbourne"'
),
'httpversion' => '1.1'
);
$mb_output = wp_remote_get($mb_url_string, $mb_args);
$mb_output = wp_remote_retrieve_body($mb_output);
return $mb_output;
}
I am using Wordpress as the backend and it does retrieve the body.
In my frontend page I call:
$mbroom = (mb_get_meetings('email#domain.tld', true, 'today 7am', 'today 7pm'));
$mbroom = json_decode($mbroom, true);
$mbroom = $mbroom['value'];
foreach ($mbroom as $k => $v) {
// get the first 3 entries
if ($k < 3) {
print_r($k);
print_r($v['subject']);
print_r(date('g:i', strtotime($v['start']['dateTime']));
print_r(date('g:i', strtotime($v['end']['dateTime']));
print_r($v['organizer']['emailAddress']['name']);
echo '<hr>';
}
}
In the results, I will get sometimes no calendar entries while other times I might get entries for 2pm but not anything from 8am. I have tried changing it to hardcoded YYYY-MM-DDD 08:00 and YYYY-MM-DD 20:00 but to no avail. I have also tried yesterday and tomorrow but none/incorrect results.
I also tried booking an entire day with 30-minute meetings and that too wasn't working.
Am I doing something wrong?

I suggest sending the startDateTime and endDateTime parameters in UTC, formatted as ISO 8601 as described in the docs (https://learn.microsoft.com/en-us/graph/api/user-list-calendarview?view=graph-rest-1.0&tabs=http), which you have done. However, I'd suggest using a PHP constant, since it's less error-prone (https://www.php.net/manual/en/class.datetimeinterface.php#datetime.constants.iso8601). Doing something like the following for those parameters:
$startDateTime = new DateTime(date("Y-m-d H:i:s"));
$startDateTime = $startDateTime->format(DATE_ISO8601);
$startDateTime = substr($startDateTime, 0, strpos($startDateTime, '+'));
Edit:
Using substring to remove the locale part of the DateTime string was probably a naïve thing to do, but it does serve the purpose.

Related

Display name of the tools provided in the last X days using XPath in PHP

Back at it again with another XML/PHP problem. :)
On my webpage I want to display the names of the tools provided in the last X days. X is a number that will be entered by the user in a textbox, and after clicking the submit button, the names of the tools that have been provided in those last X days will appear.
This will be done by comparing the X value the user enters with the dates in my XML file, and to find the tools that match.
In my XML file, I have a "dateentered" node that stores a random date that I entered:
<tools>
<tool type="..." web-based="..." free="...">
<name>Facebook</name>
<description>...</description>
<url>...</url>
<subjects>...</subjects>
<creators>...</creators>
<category>...</category>
<price>...</price>
<dateentered>2020-12-01</dateentered>
</tool>
</tools>
Next, I created a function in PHP that basically converts the 'Y-M-D' format into days by subtracting the current date from whatever date you enter:
function time2string($timeline) {
$periods = array('day' => 86400);
$ret = '';
foreach($periods AS $name => $seconds){
$num = floor($timeline / $seconds);
$timeline -= ($num * $seconds);
$ret .= $num;
}
return trim($ret);
}
Then, I loaded my xml file using simpleXML:
$xml = simplexml_load_file('tools.xml');
So for example, using the XML code sample above and doing
$days = $xml->xpath("//tool/dateentered");
foreach ($days as $day) {
print (time2string(time()-strtotime($day)));
}
this converts '2020-12-02' to '1' and therefore outputs '1', meaning that the function works as it should.
With XPath, What I want to do is, I want to compare the value the user enters in the textbox with the converted 'dateentered' from my xml, and if they match, then I want to display the tool name.
So something like:
if(isset($_REQUEST["submit"])) {
// time2string function
$f_day = $_REQUEST["days"]; // getting the value the user enters in the textbox
$xml = simplexml_load_file('tools.xml');
// do something here
}
So let's say, using the xml sample I provided above, if the user enters 1 in the textbox, the output should be:
Facebook
How can I solve this?
I'm also open for different approaches besides having to create a function, this is just what I came up with.
Turns out, like #CBroe has said, I don't even need a function that converts date to days, instead, I can take advantage of PHP's date() and strtotime() functions as follows:
<?php
if(isset($_REQUEST["submit"])) {
$xml = simplexml_load_file('tools.xml');
$f_days = $_REQUEST["days"];
$days = date("Y-m-d", strtotime("-$f_days days"));
$xdays = $xml->xpath("//tool[dateentered = '$days']/name");
foreach ($xdays as $name) {
echo "<h1 align='center'>".$name."</h1><br>";
}
}
?>
And this will output:
Facebook

Problem to convert unix time to human time

I have a online controller where i want to know the uptime and last seen of my access points. for this i use the epoch convert methode to convert unix time to human readable time.
this is the code i use
// getting controller ap info //
$name=$status=$uptime=$last_seen=[];
foreach ($ligowave->result as $index => $obj) {
$name[] = $obj->name;
$status[] = $obj->status;
$uptime[] = $obj->uptime;
$last_seen[] = $obj->last_seen;
}
// time settings //
$epoch = $uptime;
$uptimetime = (new DateTime("#$epoch"))->format(' H:i:s');
$epoch = $last_seen;
$lastseendate = (new DateTime("#$epoch"))->SetTimeZone(new DateTimeZone('Europe/Amsterdam'))->format(' d-m-Y H:i:s');
if ($status == "up") {
echo $name;
echo " is up, ";
echo "uptime is:" . $uptimetime;
} else {
echo $name;
echo " is down, ";
echo "device is last seen:" . $lastseendate;
}
return array($name, $status, $epoch, $uptimetime, $lastseendate);
}
the error i am getting is:
PHP Fatal error: Uncaught Exception: DateTime::__construct(): Failed
to parse time string (#Array) at position 0 (#): Unexpected character
Please Note:
This answer is only about resolving the specific PHP error as stated on the question. For a wider answer about how to effectively write code to iterate over arrays please see Fyrye's answer.
The error you are receiving is:
PHP Fatal error: Uncaught Exception: DateTime::__construct(): Failed to parse time string (#Array) at position 0 (#): Unexpected character
What is actually wrong, and why?
1)
You have an uncaught Exception, which throws a fatal error due to being, er, uncaught. Exceptions are central to object programming and should be researched and implemented in your PHP scripts.
2)
Why do you have an Exception in the first place? The error states the exception is caused by "Failed to parse time string (#Array)". So you are trying to give the DateTime object an array when it expects a string. It would help you to Read the PHP DateTime __construct Manual Page.
3)
Further, and more specifically the # character is unexpected; which means it should not be there. This character is only valid when followed by timestamp integer values in string format.
Because the array is output as a string (i.e in quotes) the end result is "#Array" and so the # is taken literally by DateTime; but of course this character is not expected by DateTime in any non-numeric incoming time string. This is the root cause of your fatal error here.
While PHP does employ loose typecasting to some extent, wrapping an array $var in quotes is far too loose and so the array simply outputs "Array" and issues a corresponding PHP Notice:
Notice: Array to string conversion in .....
For a valid list of correct DateTime string formats to give the object you can view this page from the PHP Manual.
4)
I do not see why you need those outer brackets?
So, how should this be done?
Reading the issues in reverse order from 4 to 1; the correct way of resolving this specific error is:
Wrap the attempt into a try/catch block to avoid these fatal errors.
Ensure that the value given to the DateTime object is a string
Remove unnecessary and invalid characters from that string.
So:
$epoch = $uptime;
try{
/***
* Uptime appears to be a numeric array of time string values
* Therefore go for the first one.
* You may want to wrap this code in a loop to catch each one.
***/
$uptimeTime = new DateTime("#".(string)$epoch[0]);
}
catch (Exception $ex){
/***
* If you wish to ignore these errors simply leave this block empty
***/
error_log("There was a problem on line ".__LINE__."! ".print_r($ex));
}
/***
* By default UTC timestamps are not zoned.
***/
// $uptimeTime->setTimeZone(new DateTimeZone('Europe/Amsterdam'));
$uptimeTimeOutput = $uptimeTime->format('H:i:s');
/***
* $uptimeTimeOutput is the correctly formatted date from $epoch[0]
***/
print $uptimeTimeOutput;
I hope with the information given above your able to correct the second DateTime instantiation code (ie the new DateTime line) yourself. :-)
TL;DR
Please read the PHP Manual and allow it to inform your coding choices.
The main issue is caused by defining $uptime[] as an array of values, resulting in $epoch = $uptime containing an array of timestamp strings. When DateTime() expects a single string value.
To resolve the issue you need to move the DateTime calls inside of the foreach iteration.
The other issue, as mentioned in the answer provided by Martin, is that you are not handling exceptions within your code. If uptime or last_seen is not of an expected value that is being supplied to the DateTime constructor, an exception will be thrown.
To handle the exceptions you can use atry/catch block in order to handle an issue that arises in your code. Exceptions are meant to point you to fatal errors in your code so that you can resolve or verify them programmatically and typically should not be ignored by using try/catch. For more details please see https://www.php.net/manual/en/language.exceptions.php
Without knowing exactly what you're trying to accomplish with your code. It appears you are wanting to echo and return all of the values from $ligowave->result. I made the appropriate changes below to reflect what I surmise are your intentions. Along with some minor simplifications.
Please clarify what you are wanting to return and echo and I will adjust my answer.
Example: https://3v4l.org/O0nS6
//...
// getting controller ap info //
$values = [];
foreach ($ligowave->result as $index => $obj) {
//convert the unix timestamps to DateTime objects
$uptime = (new DateTime('#' . $obj->uptime));
$last_seen = (new DateTime('#' . $obj->last_seen))->setTimeZone(new DateTimeZone('Europe/Amsterdam'));
//store the return values into an array
$values[] = $value = [
'name' => $obj->name,
'status' => $obj->status,
'uptimetime' => $uptime->format('H:i:s'),
'lastseendate' => $last_seen->format('d-m-Y H:i:s')
];
//output each of the statuses
printf('%s is %s, ', $obj->name, $obj->status);
if ('up' === $obj->status) {
echo 'uptime is: ' . $value['uptimetime'];
} else {
echo 'device is last seen: ' . $value['lastseendate'];
}
}
return $values;
}
Result:
foo is up, uptime is: 10:20:54
bar is down, device is last seen: 01-06-2019 08:22:30
Returns:
array (
0 =>
array (
'name' => 'foo',
'status' => 'up',
'uptimetime' => '10:20:54',
'lastseendate' => '05-06-2019 11:16:21',
),
1 =>
array (
'name' => 'bar',
'status' => 'down',
'uptimetime' => '10:20:54',
'lastseendate' => '01-06-2019 08:22:30',
),
)
It also appears that you are using a 24 hour time, to represent a duration.
If so you will need to use a DateInterval instead of DateTime, by using DateTime::diff from an appropriate timeframe. For more details please see https://php.net/manual/en/class.dateinterval.php
Assuming uptime is the started time and last_seen is the current run time, you can use $uptime->diff($last_seen), to retrieve the time that elapsed between uptime to last_seen (duration), instead of the 24 hour time value of uptime. Otherwise you can use $uptime->diff(new DateTime('now', new DateTimeZone('Europe/Amsterdam'))), to use the current date time.
One caveat, is that the hours of DateInterval are non cumulative, meaning you would need to add the days in some manner. I have used the most accurate of %a as opposed to adding on to the hours with days * 24
Example: https://3v4l.org/LHdqL
//...
$uptime = (new DateTime('#' . $obj->uptime))->setTimeZone(new DateTimeZone('Europe/Amsterdam'));
$last_seen = (new DateTime('#' . $obj->last_seen))->setTimeZone(new DateTimeZone('Europe/Amsterdam'));
$values[] = $value = [
'name' => $obj->name,
'status' => $obj->status,
'lastseendate' => $last_seen->format('d-m-Y H:i:s'),
'uptimetime' => $uptime->diff($last_seen)->format('%a.%H:%I:%S'), //DD.HH:MM:SS
];
//...
Result:
foo is up, uptime is: 12.22:48:26

Calculating RSI from Poloniex PHP

I'm trying to calculate the RSI using the Poloniex API and PHP Trader EXtension. Here is what i have so far.
date_default_timezone_set( 'UTC' );
$api = new poloniex( 'xxxxxxx', 'xxxxx' );
$data = $api->getChartValues( 'BTC_LTC', strtotime( "-21 hours" ), time(), 300 );
print_r( $data);
$rsi = array();
foreach ( $data as $a )
{
$rsi[] = $a['close'];
}
$rsi = trader_rsi( array_reverse($rsi) , 14 );
The getChartValues calls the returnChartData API Function from Poloniex API.
After running the script, the output RSI is completely different than the valid one.
What i'm doing wrong?
maybe there is no need to reverse, here is my code that works fine
$rsi = array();
foreach ( $data as $a )
{
$rsi[] = $a['close'];
}
$rsi = trader_rsi( $rsi , 14 );
print_r( $rsi );
According to the RSI definition:
The relative strength index is calculated using the following formula:
RSI = 100 - 100 / (1 + RS)
Where RS = Average gain of up periods during the specified time frame / Average loss of down periods during the specified time frame/
[...]
The default time frame for comparing up periods to down periods is 14, as in 14 trading days.
Are you sure that the RS parameter in your computation is exactly the same than in "the valid one" ? And according to you what is "the valid one" source ?

Updating YouTube PHP Function for V3 API

I had been using a function I created after piecing together different bits of code for my Wordpress blog. It allowed me to pass in a shortcode with a value defining the URL of the YouTube video, then it would print the embedded video, the view count and the duration immediately underneath it.
Unfortunately, it appears that the API I was using was depreciated by Google and I can't seem to get the current API (v3) to work with this code, so I'd appreciate some help in trying to make this right. I grabbed a developer key from Google and am able to get the JSON response when I open it directly in my browser but it seems like the calls in the code below doesn't want to match up for some reason. It would help if I still could reference the old API response to compare so I'm sort of flying blind here.
add_shortcode('yt', 'getYoutubeFrame');
// Convert Seconds to Minutes
function sec2hms ($sec, $padHours = false)
{
$hms = "";
$hours = intval(intval($sec) / 3600);
$hms .= ($padHours)
? str_pad($hours, 2, "0", STR_PAD_LEFT). ":"
: $hours. ":";
$minutes = intval(($sec / 60) % 60);
$hms .= str_pad($minutes, 2, "0", STR_PAD_LEFT). ":";
$seconds = intval($sec % 60);
$hms .= str_pad($seconds, 2, "0", STR_PAD_LEFT);
return $hms;
}
function getYoutubeFrame($atts) {
extract(shortcode_atts(array(
'video' => ''
), $atts));
// Get YouTube data via the API
$JSON = file_get_contents("https://gdata.youtube.com/feeds/api/videos?q=$video&alt=json");
$JSON_Data = json_decode($JSON);
$views = $JSON_Data->{'feed'}->{'entry'}[0]->{'yt$statistics'}->{'viewCount'};
$views = number_format($views);
$duration = $JSON_Data->{'feed'}->{'entry'}[0]->{'media$group'}->{'yt$duration'}->{'seconds'};
$s = "<iframe width=\"1280\" height=\"720\" src=\"//www.youtube.com/embed/$video?showinfo=0&rel=0&modestbranding=1&theme=light&iv_load_policy=3&autohide=1&enablejsapi=1\" frameborder=\"0\" allowfullscreen></iframe>";
$s .= "<strong>Views:</strong> $views<br><strong>Duration:</strong> ";
$s .= sec2hms($duration);
$s .= "<br /><br />";
return $s;
}
It appears that the API has changed the way the duration is displayed (using ISO 8601 instead of raw number of seconds). My experience and knowledge of PHP is rather limited otherwise I probably could have solved this in my sleep! Any suggestions for how to resolve this?
You can use the videos:list call on v3 of the YouTube API like this:
$JSON = file_get_contents("https://www.googleapis.com/youtube/v3/videos?part=statistics,contentDetails&id=$videoId&key={YOUR-API-KEY}");
$json_data = json_decode($JSON, true);
$views = $json_data['items'][0]['statistics']['viewCount'];
$duration = $json_data['items'][0]['contentDetails']['duration'];
This will get the number of views and the duration of the video specified in $videoId.
The $duration is going to come back in ISO 8601 format which looks something like PT4H48M18S. You can pass this into a DateInterval class and then format as you wish. For instance:
$dateInterval = new DateInterval($duration);
$formattedDuration = $dateInterval->format("%H:%I:%S");
would take PT4H48M18S and make it look like this 04:48:18.

Laravel and Charting - Determine The Difference Between Datatimes

I've been trying to create a chart similar to http://bit.ly/1jF6AY7 for a few weeks now using the Highcharts library. However, it does not understand that there should be zero values (if no value / date is provided) for the following series:
12-18-13
01-01-14
01-03-14
01-06-14
01-15-14
However, clearly using Google charts it is possible as the above graph is showing the gaps properly between the "contributed" dates. I need to do the same for my created_at dates. Any suggestions? I'd prefer to do this using Highcharts, but at this point my stress and hair is more valuable than anything else ;)
Vanilla PHP code example:
$q = $_GET['search'];
if(isset($_GET['time'])) {
$time = $_GET['time'];
} else {
$time = NULL;
}
include_once('../../vendors/HighchartsPHP-master/Highchart.php');
$chart = new Highchart(Highchart::HIGHSTOCK);
$chart->includeExtraScripts();
$chart->chart->renderTo = "chart";
$chart->title->text = "Potential Impact for \${$q}";
$chart->rangeSelector->selected = 1;
$chart->xAxis->type = 'datetime';
$chart->xAxis->gapGridLineWidth = 3;
$chart->xAxis->dateTimeLabelFormats->hour = "%H:%M";
$chart->xAxis->dateTimeLabelFormats->month = "%e. %b";
$chart->xAxis->dateTimeLabelFormats->year = "%b";
// $chart->xAxis->tickInterval = 3600 * 1000;
$chart->yAxis->title->text = "Volume";
$chart->yAxis->min = 0;
$chart->credits->enabled = false;
// $chart->rangeSelector->buttons[] = array(
// 'type' => "minute",
// 'count' => 5,
// 'text' => "5"
// );
...
$chart->rangeSelector->buttons[] = array(
'type' => "month",
'count' => 6,
'text' => "6M"
);
$chart->rangeSelector->buttons[] = array(
'type' => "year",
'count' => 1,
'text' => "1Y"
);
$chart->rangeSelector->buttons[] = array(
'type' => "all",
'text' => "All"
);
$option = new HighchartOption();
$option->global->useUTC = false;
Highchart::setOptions($option);
$chart->series[] = array(
'name' => "Potential Impact for \${$q}",
'data' => new HighchartJsExpr("data"),
'step' => 1,
'gapSize' => 5,
'connectNulls' => false,
'tooltip' => array(
'valueDecimals' => 0
)
);
$data = format_data($q, $time);
$chart->series[0]->data = $data;
?>
<div id="chart"></div>
<script type="text/javascript"><?php echo $chart->render("chart1"); ?></script>
And, the format data function:
function format_data($q, $time = NULL) {
$msg_vol = correlate_created_at_with_msg_vol($q, $time);
while ($tweet = mysqli_fetch_array($msg_vol, MYSQL_ASSOC)) {
$date = $tweet['date'] * 1000;
$count = intval($tweet['count']);
$formatted[] = array($date, $count);
}
return $formatted;
}
In the above picture, you can see not only is it graphing single data points (as illustrated by the tooltip) without showing any visual reference on the chart, but it is skipping the null periods, and not displaying the respective times properly on the xAxis. The odd part is that if you reference the bottom bar which allows the user to change the time period by dragging the bar... That bar has the gaps properly illustrated like what I'm looking for.
If I can't get Highcharts to go to a zero value for the gaps, I at the very least, need it to show the time intervals without jumping by an unpredictable number of minutes, hours, and in some cases, days and weeks.
If you use a datetime based series and there is no data for those points highcharts should handle it. Without seeing your code I image you are doing a category based xAxis and you are not "binning" any data in this time slot.
Edited:
Quick glance at your pasted code shows you are using HighStock, this is good. You can use the ordinal setting. Set this to false and you should see the gaps. Now it is still hard to say as I cannot see what your data is really but this should give us something to go on. You may also want to enable connectNulls.
Easy Solution, force 0 if there is no data.

Categories