logic to loop through records containing timestamps - php

I need help with some logic when handling timestamps.
I have a table with a few hundred records, each record has a field witch contains a timestamps.
I have $NextAuditStamp, this field is populated via a user input script which converts dates to timestamps.
Now I need to loop through each record and return all the records where the $NextAuditStamp minus $n is greater than $NowTime. Here is the test code I am currently work with to try and get the logic working:
$NowTime = time();
$Flag = "";
$n = 2635250; // this is a fixed timestamp representing 1 month
$NextAuditStamp = strtotime($_POST['NextAuditDate']);
if($NowTime - $n > $NextAuditStamp) {
$Flag = 1;
} elseif($NowTime > $NextAuditStamp) {
$Flag = 2;
} else {
$Flag = "0";
}

$NextAuditStamp minus $n is greater than $NowTime
Your test for $Flag = 1 does the opposite, guess you want
if($NextAuditStamp - $n > $NowTime) {...}

Related

Laravel group data by odd amount of hours throughout days

I'm trying to group some data in my Laravel project by a date format that is a bit different to the norm. I've got a database that and a query that fetches "Uptime Checks" for a user's website based on the period they want to look over, I then need to display this to the user as some kind of timeline.
In order to reduce "noise" in the data (where there may not be enough uptime checks for a given period) I'd like to group all of my results within say a 3 hour period throughout the day, so I'd have all of the data for:
2021-05-02 03:00:00
2021-05-02 06:00:00
2021-05-02 09:00:00
and so on, right now I'm bringing back data by the hour, but not sure how to modify this to achieve the desired outcome
// get the uptime checks for past X hours
$uptimeData = UptimeChecks::where('user_id', 1)
->where('monitor_id', 1)
->where('checked_at', '>=', '2021-05-02 13:00:00')
->where('checked_at', '<=', '2021-05-03 13:00:00')
->orderBy('checked_at', 'asc')
->select('event', 'checked_at')
->get();
$uptimeDataTimeline = $uptimeData->groupBy(function ($item, $key) {
$date = Carbon::parse($item->checked_at);
// group by hour, how can I get say every 3 hours worth of data?
return $date->format('Y-m-d H:00:00');
});
$uptimeDataTimeline = $uptimeDataTimeline->map(function ($checksInPeriod, $key) {
$down = 0;
$up = 0;
$total = 0;
$uptime = 0;
$fill = '#1fc777'; // green
// $checksInPeriod is all of the data for a given hour at the moment
// I need to group by a bigger period, say, every 3 hours
// add our events
foreach ($checksInPeriod as $key => $value) {
$total++;
if (strtolower($value['event']) == 'down') $down++;
if (strtolower($value['event']) == 'up') $up++;
}
// calculate uptime
$uptime = floatval(number_format(round($up / $total, 5) * 100, 2, '.', ','));
// fill colours
if ($uptime < 100) $fill = '#9deab8'; // lighter green
if ($uptime < 99) $fill = '#fbaa49'; // amber
if ($uptime < 98) $fill = '#e0465e'; // red
return [
'total_events' => $total,
'down_events' => $down,
'up_events' => $up,
'uptime' => $uptime,
'fill' => $fill
];
});
Not sure how to modify the groupBy function which returns the format since my understanding is that it's not possible to do that? I'm using Carbon by the way.
Update
I've been digging, and have come across the CarbonInterval feature, which allows me to generate some intervals, and I've tried implementing this, I seem to get an equally spaced time period, but my data is out and doesn't contain all of the data between two intervals (see attached image)
$intervals = CarbonInterval::hours(2)->toPeriod($from, $to);
$uptimeDataTimeline = $uptimeData->groupBy(function ($item, $key) use ($intervals) {
$date = Carbon::parse($item->checked_at);
foreach ($intervals as $key => $interval) {
if ($date->hour == Carbon::parse($interval)->addHours(1)->hour) {
$actualHour1 = Carbon::parse($interval)->hour;
if (strlen($actualHour1) == 1) $actualHour1 = "0$actualHour1";
return $date->format("Y-m-d $actualHour1:00:00");
} else if ($date->hour == Carbon::parse($interval)->addHours(2)->hour) {
$actualHour2 = Carbon::parse($interval)->subHours(2)->hour;
if (strlen($actualHour2) == 1) $actualHour2 = "0$actualHour2";
return $date->format("Y-m-d $actualHour2:00:00");
}
}
return $date->format('Y-m-d H:00:00');
});
For instance, I should be seeing all of the checks for the hours 7 and 8 within the 07 key, but instead I'm seeing data for just one hour (hour 11)?
The best thing to use whenever you need time slice(s) is DateInterval or better CarbonInterval. What they give you is the ability to loop over those slices and do equality/unequlity operation of your sample data this way you can easily organise your data by those time slices to their respective "slots"
Here is an general idea on how to
$intervals = \Carbon\CarbonInterval::hours(3)->toPeriod('2021-05-02 13:00:00', '2021-05-03 13:00:00');
//we get time slots of 3 hours between provided datetimes
foreach ($intervals as $date) {
$dtArr[] = strtotime($date->format('Y-m-d H:i:s')); //we collect those "time markers"
}
$result = [
'first'=> 0,
'second'=>0.
'third'=>0,
'forth'=>0,
'fifth'=>0,
'sixth'=>0,
'seventh'=>0,
'eighth'=>0
]; //array to accumulate your aggregations to correct time slot
foreach ($uptimeData as $sample) {
//loop over sample set
$ordinality = getSlotNo($sample->checked_at); //eg. third
//read the accumulated total in $result and add this too
$result[$ordinality] += 1;
}
function getSlotNo($dt){
$ts = strtotime($dt);
//eg. say greater than or equal to "13:00" but smaller than "16:00" -> You go in first slot
if($ts>=$dtArr[0] && $ts<$dtArr[1]){
//first slot
return 'first';
}
elseif($ts>=$dtArr[1] && $ts<$dtArr[2]){
//eg. say greater than or equal to "16:00" but smaller than "19:00" -> You go in second slot
//second slot
return 'second';
}
elseif($ts>=$dtArr[2] && $ts<$dtArr[3]){
//third slot
return 'third';
}
// and so on
}
UPDATE
Try something like this may be, modify the slot getter to "look ahead" and decide the result
$i=0;
foreach ($intervals as $date) {
$dtArr[] = strtotime($date->format('Y-m-d H:i:s')); //we collect those "time markers"
$result['int_'.$i] = 0;
$i++;
}
//fake data
$uptimeData=collect([
(object)['checked_at'=>'2021-05-03 10:10:00'],
(object)['checked_at'=>'2021-05-03 11:20:00'],
(object)['checked_at'=>'2021-05-03 12:20:00'],
(object)['checked_at'=>'2021-05-03 13:20:00'],
(object)['checked_at'=>'2021-05-03 14:20:00'],
]);
foreach ($uptimeData as $sample) {
//loop over sample set
$ordinalInfo = getSlotNo($sample->checked_at, $dtArr); //eg. third
//read the accumulated total in $result and add this too
if($ordinalInfo['match']){
$result['int_'.$ordinalInfo['index']] += 1;
}
}
/**
* #param $dt
* #return int index in $dtArr this value belongs to
*/
function getSlotNo($dt, $dtArr){
$ts = strtotime($dt);
$info = [];
for($i =0; $i<count($dtArr); $i++){
if(!empty($dtArr[$i+1])){ // if not reached the last item ie. still there's a next
if($ts>=$dtArr[$i] && $ts<$dtArr[$i+1]){
//i'th slot
$info=['match'=>true,'index'=>$i];
break;
}
}else{
// at last item ie. ( $i == count($dtArr)-1 )
if($ts<=$dtArr[$i])
$info=['match'=>true,'index'=>$i];
else
$info=['match'=>false,'index'=>NULL];
}
}
return $info;
}

Comparing values from a database using a while loop

I have a MySql table where I saved all workers names and the dates workers have to work on. I want to show a list containg all days of the current month and the worker names who have to work on the day that corresponds to them. Example:
February
1
2
3 - John Wick
5
6 - Martha Beck
etc.
This is the code I have in PHP but the loop is not working. I just get a list from 1 to 30 but it is not showing the data from database. If I run the loop without the (while ($n < 31)), I get all the records from database but I want to show the names just beside the day that correspond.
<?php
mysql_select_db($database_nineras, $nineras);
$query_res = sprintf("SELECT res_id, res_dateini, res_datefin, res_name FROM reservas ORDER BY res_dateini DESC");
$reservas = mysql_query($query_res, $nineras) or die(mysql_error());
$rreser = mysql_fetch_assoc($reservas);
$treser = mysql_num_rows($reservas);
$n = 1;
while ($n < 31) {
do {
++$n;
if ($n == date('d', strtotime($rreser['res_dateini']))) {
echo $n . ' - ' . $rreser['res_name'];
}
else {
echo $n;
}
} while ($rreser = mysql_fetch_assoc($reservas));
}
?>
The problem with your code is that the do-while loop is fetching all the rows returned by the query. So when you get to the second iteration of the while loop there's nothing left to fetch.
Rather than fetch the rows from the database each time through the loop, you can fetch them once and put them into an array whose index is the day numbers. Then you can loop through the days and print all the rows for each day.
Use date('j', ...) to get the date without a leading zero. Or change your SQL query to return DAY(res_dateini).
$results = array();
$reservas = mysql_query($query_res, $nineras) or die(mysql_error());
while ($rreser = mysql_fetch_assoc($reservas)) {
$d = date('j', strtotime($rreser['res_dateini'])));
$results[$d][] = $rreser['res_name'];
}
for ($day = 1; $day <= 31; $day++) {
echo "$day - " . (isset($results[$day]) ? implode(", ", $results[$day]) : "") . "<br>\n";
}
DEMO

Date Comparison to update records

I'm getting records from a MySQL database with this PHP function:
function someFunction($date){
// all distinct records
$query = "select count(distinct column_name) as alias from table_name where DATE(date_column) = '$date'";
$result = $connection->query($query);
$row = $result->fetch_assoc();
return $row['alias'];
// end of all distinct records
}
Now what the below PHP code does is, get the day in the date, compute the week of the month it belongs to and stores it an an array.
//while fetch_assoc returns records
//$result1 query: "select * from table_name where DATE(date) between '$first_date' and date_add('$end_date',interval 1 day)"
while ($row1 = $result1->fetch_assoc()) {
$date = $row1['date'];
$start = 1;
$end = 7;
for ($i = 1; $i <= 5; $i++) {
if ((int) date('d', strtotime($date)) >= $start && (int) date('d', strtotime($date)) <= $end) {
if (!isset($arr1[$i]) || !isset($arr2[$i])) {
$arr1[$i] = 0;
$arr2[$i] = 0;
}
++$arr1[$i];
$arr2[$i] = someFunction(date('Y-m-d', strtotime($date)));
}
$start += 7;
$end += 7;
}
}
Consider 1st, 2nd and 3rd belong to the same week, 1st has 3 records, 2nd has 4 and 3rd has 1. The while loop will iterate 7 times, each value returned by the someFunction() overwriting the value in $arr2[$i].
So my question is, how will I be able to check if the previous iteration date value is equal to the current date value?
So my question is, how will I be able to check if the previous iteration date value is equal to the current date value?
Pseudocode:
$lastValue = …; // initialization with a value that does not occur in the actual values,
// such as NULL, empty string, …
while(…) {
if($currentValue == $lastValue) {
// do something
}
else {
// do something else
}
// …
$lastValue = $currentValue; // set current value for next loop interation
}

How can I generate a random number every x amount of months?

Starting with the number 9 and using php, I would like to be able to count up from there, and echo out the next number in increments of 1.
So, number 9, then after 1 month the number would change to 10, then another month 11, then 12 etc., with no maximum number/stop point.
How can I accomplish this? So far I have the below code.
$number = 9;
$output = $number + 1;
echo $output;
Is there a way to set this to increase once a month?
You can do this with the PHP date()-function. This is one example of doing it if you are not dependent on the day of the month, but adding day functionality is possible and should be quit easy.
$startNumber = 9;
$startYear = 2015;
$startMonth = 9;
$currentYear = intval( date( "Y" ) );
$currentMonth = intval( date( "n" ) );
$monthsToAdd = ( ( $currentYear - $startYear ) * 12 )
+ ( $currentMonth - $startMonth );
echo $startNumber + $monthsToAdd;
From your question, I'd say:
$number = 9;
$output = date('n') + $number;
echo $output;
But that depends on what you are trying to accomplish. You can also wrap the number around the date() with a modulo.
However this is nothing random. If you want to create a random number every month like your topic suggests, use the month as the random seed.
srand(date('n'));
$number = rand();
a very inefficient way would be
<?php
function increm($duration){
while ($i<$duration) {
$i++;
}
return true;
}
$number = 9;
$start = time();
$i = 0;
while (1){
increm(3600*24*30);
$i++;
// Do your code
}
?>
this script would have to be run continuously for months.
A better way would be
<?php
$number = 9;
if(!file_exists('date.txt')){
$date=date('n');
file_put_contents( (string)time());
$date = 0;
}
else{
$date= file_get_contents('date.txt');
$date= date()-(int)$date;
$date= floor($date/(24*3600*30));
}
// do whatever you may
?>
But this script would increase it whenever called as the first open date would be stored. Will work forever (till UNIX can timestamp).
for this purpose you have to store the number in the database, compare with current unix timestamp and update it when the new month is reached.
2 database columns: count_month int(10) and next_month int(10) where next_month will contain the unix timestamp of the first day of the next month. you can run it with cronjobs or on production.
<?php
$now = strtotime("now");
$next_month = strtotime("first day of next month");
if ($query = $dbconnect->prepare("SELECT next_month FROM table1")) {
$query->execute();
$query->bind_result($compare_time);
$query->store_result();
$row_count = $query->num_rows;
if ($row_count > 0) {
while ($query->fetch()) {
if ($compare_time < $now) { // you reached the 1th of the next month time to update
if ($query2 = $dbconnect->prepare("UPDATE table1 SET count_month=count_month +1, next_month=?")) {
$query2->bind_param('i', $next_month);
$query2->execute();
$query2->close();
}
}
}
}
$query->free_result();
$query->close();
}
?>

AJAX disabledDates Fullcalendar Machines/Users

i have multiple jquery fullcalendars on a page which is used for machine scheduling in our software.
each calender will make a ajax call once a specific input for the workload changes.
In my AJAX script i need to calculate the dates that will be disabled in the calendar based upon the already planned time of the machine and whether or not there are workers with enough time avail on that day.
so here is what i got so far:
$retval = Array();
$objectid = $_REQUEST["objectid"];
$type = $_REQUEST["type"];
$amount = $_REQUEST["amount"];
$date = $_REQUEST["date"];
$month = date("m",strtotime($date));
$year = date("Y",strtotime($date));
if ($month == date("m") && $year == date("Y"))
{
$start = mktime(0,0,0,date("m"),date("d"),date("Y"));
} else {
$mktime1 = mktime(0,0,0,$month,24,$year);
$start = strtotime('-1 month',$mktime1);
}
$mktime2 = mktime(0,0,0,$month,6,$year);
$end = strtotime('+2 month',$mktime2);
$job_arr = Array();
$jobs = PlanningJob::getAllJobs(" AND start>".$start." AND end<".$end);
foreach ($jobs as $job)
{
$date = mktime(0,0,0,date("m",$job->getDate()),date("d",$job->getDate()),date("Y",$job->getDate()));
$job_arr[$date] = 0;
if ($job->getTime()>0)
$job_arr[$date] += $job->getTime();
elseif ($job->getPlannedTime()>0)
$job_arr[$date] += $job->getPlannedTime();
}
if ($type == "ME"){
$me = new Machineentry($objectid);
$machine = $me->getMachine();
for ( $i = $start; $i <= $end; $i = $i + 86400 ) {
$date = mktime(0,0,0,date("m",$i),date("d",$i),date("Y",$i));
$total_seconds = $machine->getRunningtimeForDay($date);
if (isset($job_arr[$date]))
{
$total_seconds -= ($job_arr[$date]*60*60);
}
if ($total_seconds < ($amount*60*60))
$retval[] = date("d.m.Y",$date);
}
} else if ($type == "OP")
{
$op = new Orderposition($objectid);
$jobart = new Article($op->getObjectid());
}
now i wonder if there is a better approach to calculating for so many dates (date range) at once - performance wise. as this one is quite heavy and takes a couple of seconds which really slows down our scheduling process.
i would appreciate any help/advice!
Kind Regards,
Alex
[EDIT] some more info's on what i need to output:
One Array containing: all dates within the range
and within this array per date:
1. all users having spare time on that day and the specific amount of time
2. total time scheduled on the machine that day
Second Array
Per date: if not enough 'free' time on the machine only the date so it can be disabled in the calendar
Here is a function I wrote a while back which will take two dates and return an array of the range between them. I use this function on a variety of things and it's very fast.
Dates provided for $dateFrom and $dateTo should be in YYYY-MM-DD format.
function createDateRangeArray($dateFrom,$dateTo)
{
$arrayRange=array();
$dateFrom=mktime(1,0,0,substr($dateFrom,5,2), substr($dateFrom,8,2),substr($dateFrom,0,4));
$dateTo=mktime(1,0,0,substr($dateTo,5,2), substr($dateTo,8,2),substr($dateTo,0,4));
if ($dateTo>=$dateFrom)
{
array_push($arrayRange,date('Y-m-d',$dateFrom));
while ($dateFrom<$dateTo)
{
$dateFrom+=86400;
array_push($arrayRange,date('Y-m-d',$dateFrom));
}
}
return $arrayRange;
}

Categories