Remove dates array between start date and end date in php - php

I want to find an optimized way to remove dates between start date and end date in php, below how i handle it but seems timeout when there is too many days :
/**
* Get list of dates from start date and end date
* #param {string} start
* #param {string} end
* #param {string} format
* #return array
*/
function _getDatesFromRange($start, $end, $format = 'd/m/Y') {
// Declare an empty array
$array = array();
// Variable that store the date interval
// of period 1 day
$interval = new DateInterval('P1D');
$realEnd = DateTime::createFromFormat('Y-m-d', $end);
$realEnd->add($interval);
$period = new DatePeriod(DateTime::createFromFormat('Y-m-d', $start), $interval, $realEnd);
// Use loop to store date into array
foreach($period as $date) {
$array[] = $date->format($format);
}
// Return the array elements
return $array;
}
/**
* Flat an array
* #param {array} array
* #return array
*/
function _flatten($array) {
$return = array();
array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
return $return;
}
// List of dates
$bookings = array(
array(
'bookable' => 'no',
'from' => '2020-08-01',
'to' => '2020-08-05'
),
array(
'bookable' => 'no',
'from' => '2020-08-15',
'to' => '2020-08-18'
),
array(
'bookable' => 'yes',
'from' => '2020-08-01',
'to' => '2020-08-31'
)
);
So to list all dates and get bookable list, i do like this :
foreach($bookings as $booking){
if($booking['bookable'] === 'yes'){
// Get an array of list of dates between start and end
$bookable[] = _getDatesFromRange($booking['from'], $booking['to']);
} else {
$not_bookable[] = _getDatesFromRange($booking['from'], $booking['to']);
}
}
if(is_array($bookable) && is_array($not_bookable)) {
$output = array_diff(_flatten($bookable), _flatten($not_bookable));
print_r($output);
}
You can test all from this url bookable dates demo, i have 2000 products and some products have a large intervals between start and end date like below, and in this case i get timeout execution, so how i can optimise above code ?
$bookings = array(
array(
'bookable' => 'no',
'from' => '2020-08-01',
'to' => '2020-08-05'
),
array(
'bookable' => 'no',
'from' => '2020-08-15',
'to' => '2020-08-18'
),
array(
'bookable' => 'yes',
'from' => '2050-08-01',
'to' => '2050-08-31'
)
);
Thanks for you helps

Related

PHP returning an element from an array

So I have some code that returns all the time in an array like this Open hours today: 9:00- 9:45, 9:55 - 10:20, 10:30 - 11:00 . If we used $formatted_ranges[array_key_first($formatted_ranges)] instead of join, it would return a single element as like this, "Open hours today: 9:00 - 9:45". However we need to return like this,
Open hours today: 9:00 - 11:00.
$start = DateTime::createFromFormat( 'H:i', $range['from'] );
$end = DateTime::createFromFormat( 'H:i', $range['to'] );
$formatted_ranges = array_map( function( $range ) {
return $this->format_time( $range['from'] ).' - '.$this->format_time($range['to'] );
}, $ranges );
return sprintf(
__( 'Open hours today:', 'example' ) . ' <span>%s</span>',
join( ', ', $formatted_ranges )
);
If I understand the input data correctly, you don't need to iterate and reformat every element in the multidimensional array.
Just access the from from the first row and the to from the last row and you're done. Format just those two values if necessary.
Code: (Demo)
$ranges = [
['from' => '9:00', 'to' => '9:45'],
['from' => '9:55', 'to' => '10:20'],
['from' => '10:30', 'to' => '11:00'],
];
if (!isset($ranges[0]['from'], $ranges[0]['to'])) {
throw new Exception('insufficient business hours data');
}
printf(
'Open hours today: %s - %s',
$ranges[0]['from'],
$ranges[array_key_last($ranges)]['to']
);
Output:
Open hours today: 9:00 - 11:00
The originally shared code was not runnable. From the question, I think you want to reformat a time range array to find the beginning and the end of all the ranges.
As long as all the time are represented as 24-hour clock format, this is an example of how it can be done.
<?php
/**
* Convert multiple ranges into a single range.
*
* #param array $ranges
* #return array
*/
function overallRanges(array $ranges): array {
if (sizeof($ranges) === 0) {
throw new \Exception('The provided ranges array is empty');
}
$range = array_reduce($ranges, function ($carry, $current) {
$from = DateTime::createFromFormat('H:i', $current['from']);
$to = DateTime::createFromFormat('H:i', $current['to']);
$carry['from'] = ($from < $carry['from']) ? $from : $carry['from'];
$carry['to'] = ($to > $carry['to']) ? $to : $carry['to'];
return $carry;
},
[
'from' => DateTime::createFromFormat('H:i', $ranges[0]['from']),
'to' => DateTime::createFromFormat('H:i', $ranges[0]['to']),
]
);
return [
'from' => $range['from']->format('G:i'),
'to' => $range['to']->format('G:i'),
];
}
// example use
$ranges = [
['from' => '9:00', 'to' => '9:45'],
['from' => '9:55', 'to' => '10:20'],
['from' => '10:30', 'to' => '11:00'],
];
var_dump(overallRanges($ranges));
The output:
array(2) {
["from"]=>
string(5) "9:00"
["to"]=>
string(5) "11:00"
}
Should be a good enough start for you to reformat into anything.

PHP end() function not getting end Array item and object key in Laravel

In my Laravel project, I've got a job set up which runs and attempts to notify a user based on their threshold and chosen alert metrics. I'm using the php end() method to get the last item in an array and then attempting to get whatever metric the user has chosen.
However, upon dumping the data, this isn't returning the last array item, it's returning every item and I'm not sure why?
When I dump my data, I'm getting this format instead of the last item in the array:
[2021-04-13 13:30:45] production.DEBUG: array (
0 =>
(object) array(
'event_category' => 'My Category',
'event_action' => 'My Event',
'event_count' => '2190',
'period_from' => '2021-04-13 00:00:00',
'period_to' => '2021-04-13 13:30:02',
'created_at' => '2021-04-13 13:30:06',
),
1 =>
(object) array(
'event_category' => 'My Category',
'event_action' => 'My Event',
'event_count' => '5184',
'period_from' => '2021-04-12 00:00:00',
'period_to' => '2021-04-12 23:57:02',
'created_at' => '2021-04-12 23:57:07',
),
2 =>
(object) array(
'event_category' => 'My Category',
'event_action' => 'My Event',
'event_count' => '3820',
'period_from' => '2021-04-11 00:00:00',
'period_to' => '2021-04-11 23:57:02',
'created_at' => '2021-04-11 23:57:07',
),
)
I should just be seeing the last item, amongst all of my code, the following is of significant value here:
/**
* Notify if data meets threshold & alert rules
*
* #return void
*/
public function notifyAlertThreshold($alerts, $data)
{
$newestDataPart = end($data) ?? null;
// alerts for data source
foreach ($alerts as $key => $alert) {
Log::debug($newestDataPart);
$metric = !isset($newestDataPart->{$alert->metric}) ? $newestDataPart : $newestDataPart->{$alert->metric};
}
}
In context, here's some mode of the code, but the primary question here, is why is my end() method not returning the last item?
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$filters = json_decode($this->report->discovery_filters, true);
$this->reportStatus = 'complete';
$data = [];
foreach ($filters as $findableKey => $findable) {
/*
** If there are datasets on the findable objec, then we assume
** that we can build up a chart or some data structure.
*/
if (isset($findable['datasets'])) {
$pushableDatasets = [];
foreach ($findable['datasets'] as $datasetKey => $dataset) {
// query data
if (isset($dataset['query'])) {
$chartLabel = $findable['name'] ?? 'Untitled Chart';
$this->setDynamicChartOptions($chartLabel);
$additionFromField = $dataset['query']['additionFromField'] ?? '';
$resultData = [];
if ($dataset['query']['prefersConversionCalculation'] == 'yes') {
$totals = DB::table($dataset['query']['table'])
->select($dataset['query']['columns'])
->where($dataset['query']['calculateConversionFromTotals'])
->orderBy($dataset['query']['orderBy']['field'], $dataset['query']['orderBy']['direction'])
->get()
->chunk(100);
$goal = DB::table($dataset['query']['table'])
->select($dataset['query']['columns'])
->where($dataset['query']['calculateConversionByGoal'])
->orderBy($dataset['query']['orderBy']['field'], $dataset['query']['orderBy']['direction'])
->get()
->chunk(100);
$totals = $totals->flatten();
$goal = $goal->flatten();
$totalsGrouped = $this->groupData(
$totals,
$dataset['query']['groupBy'],
$dataset['query']['groupByFormat'],
$additionFromField
);
$goalsGrouped = $this->groupData(
$goal,
$dataset['query']['groupBy'],
$dataset['query']['groupByFormat'],
$additionFromField
);
$totalsGroupedFlattened = $totalsGrouped->flatten();
$goalsGroupedFlattened = $goalsGrouped->flatten();
$resultData = $this->getStructure($findable, $datasetKey, $goalsGroupedFlattened, $totalsGroupedFlattened);
array_push($pushableDatasets, $resultData);
} else {
$res = DB::table($dataset['query']['table'])
->select($dataset['query']['columns'])
->where($dataset['query']['filterBy'])
->orderBy($dataset['query']['orderBy']['field'], $dataset['query']['orderBy']['direction'])
->get()
->chunk(100);
$res = $res->flatten();
if (isset($dataset['query']['useGrouping']) && $dataset['query']['useGrouping'] == 'yes') {
$results = $this->groupData(
$res,
$dataset['query']['groupBy'],
$dataset['query']['groupByFormat'],
$additionFromField
);
// if we're using an addition function our array is already flattened
if (!empty($additionFromField)) {
$resultData = $results;
} else {
$resultData = $results->flatten();
}
array_push($pushableDatasets, $this->getStructure($findable, $datasetKey, $resultData));
}
}
$dataForAlerts = $resultData;
if ($dataset['query']['prefersConversionCalculation'] == 'yes') {
$dataForAlerts = $dataForAlerts['data'];
}
// alerting
$alerts = $this->getAlertThresholds($dataset['query']['table']);
$this->notifyAlertThreshold($alerts, $dataForAlerts);
}
}
$findable['datasets'] = $pushableDatasets;
}
array_push($data, $findable);
}
// no data or it's empty
if (!isset($data) || empty($data)) {
$this->reportStatus = 'error';
}
// create our report data entry
$this->updateReportData(false, $data);
}

Splitting date and time then put into other column [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I'm working a small date time project. From my CSV file I have 4 columns, called Name, Employee ID, Data/Time, Status and in my Database columns are name, empID, date, timeIn, timeOut, Status.
Picture of my csv file: (Picture A)
And here is what I want to save in my database: (Picture B)
Example input CSV string:
Name,"Empoyee ID",Date/Time,Status
"Soriano, Jhoniel",901,"05/03/2019 1:01:03 PM",C/Out
"Soriano, Jhoniel",901,"05/04/2019 2:01:03 PM",C/In
"Soriano, Jhoniel",901,"05/04/2019 3:01:03 PM",C/Out
"Soriano, Jhoniel",901,"05/06/2019 4:01:03 PM",C/In
"Soriano, Jhoniel",901,"05/06/2019 5:01:03 PM",C/Out
"Soriano, Jhoniel",901,"05/07/2019 6:01:03 PM",C/In
How can I get started on this problem?
Edit
I want to thanks #jimmix for giving me some idea to get started.
Here is the real scenario:
From my CSV file, I have the data you can found at (Picture A), then I will upload using my upload() function in into my MySQL database with the table name "tbldumpbio",
See the table structure below:
From my table tbldumpbio data, I have a function called processTimesheet()
Here's the code:
public function processTimesheet(){
$this->load->model('dbquery');
$query = $this->db->query("SELECT * FROM tbldumpbio");
foreach ($query->result() as $row){
$dateTimeExplArr = explode(' ', $row->datetimex);
$dateStr = $dateTimeExplArr[0];
$timeStr = $dateTimeExplArr[1];
if($row->status='C/Out' and !isset($timeStr) || empty($timeStr) ){
$timeStrOut ='';
} else {
$timeStrOut = $dateTimeExplArr[1];
}
if($row->status='C/In' and !isset($timeStr) || empty($timeStr) ){
$timeStrIn ='';
} else {
$timeStrIn = $dateTimeExplArr[1];
}
$data = array(
'ID' => '',
'companyAccessID' => '',
'name' => $row->name,
'empCompID' => $row->empid,
'date' => $dateStr,
'timeIn' => $timeStrIn,
'timeOut' => $timeStrOut,
'status' => '',
'inputType' => ''
);
$this->dbquery->modInsertval('tblempbioupload',$data);
}
}
This function will add a another data into tblempbioupload. But here are the results that I'm getting with:
Please see the below data:
The problem is:
the date should not be duplicated
Time In data should be added if the status is 'C/In'
Time Out data should be added if the status is 'C/Out'
The expected result should be something like this:
Not the shortest one but working:
<?php
$csvMultilineStr =
'Name,"Empoyee ID",Date/Time,Status
"Soriano, Jhoniel",901,"05/03/2019 1:01:03 PM",C/Out
"Soriano, Jhoniel",901,"05/04/2019 2:01:03 PM",C/In
"Soriano, Jhoniel",901,"05/04/2019 3:01:03 PM",C/Out
"Soriano, Jhoniel",901,"05/06/2019 4:01:03 PM",C/In
"Soriano, Jhoniel",901,"05/06/2019 5:01:03 PM",C/Out
"Soriano, Jhoniel",901,"05/07/2019 6:01:03 PM",C/In';
//read CSV as every line = 1 array item
$csvArr = explode("\n", $csvMultilineStr);
//get header line as array of column names
//and remove that line from $csvArr
$headerArr = str_getcsv(array_shift($csvArr));
$emplIdKeyStr = "Empoyee ID";
$resultArr = [];
/**
* Create array of arrays
* that each array inside an array
* is under key of emploeeId and
* represents one CSV row
*
* [
* 901 => [
* 0 => [
* 'Name' => value
* 'Empoyee ID' => value2
* ...
* ],
* 1 => [
* 'Name' => value
* 'Empoyee ID' => value2
* ...
* ]
* ]
* ]
*
*/
foreach($csvArr as $csvLineStr) {
$csvEntryArr = str_getcsv($csvLineStr);
$csvLineArr = array_combine($headerArr, $csvEntryArr);
$emplIdInt = $csvLineArr[$emplIdKeyStr];
$resultArr[$emplIdInt][] = $csvLineArr;
}
var_export($resultArr);
/**
* Create array of arrays
* as above but with key => value
* structure
*
* [
* 901 => [
* '05/03/2019' => [
* 'C/In' => ...
* 'C/Out' => ...
* 'name' => ...
* ]
* ]
* ]
*
*/
$resultTimeIoArr = [];
$timeInOutColSwStr = 'Status';
$dateTimeColStr = 'Date/Time';
foreach($resultArr as $emplIdInt => $emplEntyArr) {
foreach($emplEntyArr as $emplSingleEntryArr) {
$dateTimeStr = $emplSingleEntryArr[$dateTimeColStr];
$dateTimeExplArr = explode(' ', $dateTimeStr);
$dateStr = $dateTimeExplArr[0];
$timeStr = $dateTimeExplArr[1];
$resultTimeIoArr[$emplIdInt][$dateStr][$emplSingleEntryArr[$timeInOutColSwStr]] = $timeStr;
$resultTimeIoArr[$emplIdInt][$dateStr]['name'] = '"' . $emplSingleEntryArr['Name'] . '"';
}
}
/**
* get sorted array by EmplId, Date, C/In or C/Out
* as an arry similar to first one CsvArr
*/
$resultCsvArr = [];
$resultCsvLineArr = [];
foreach($resultTimeIoArr as $emplIdInt => $singleEmplArr) {
foreach($singleEmplArr as $dateStr => $signleIoArr) {
$resultCsvLineArr['Name'] = $signleIoArr['name'];
$resultCsvLineArr['EmpID'] = $emplIdInt;
$resultCsvLineArr['Date'] = $dateStr;
if(!isset($signleIoArr['C/In']) || empty($signleIoArr['C/In'])) {
$timeIn = '';
} else {
$timeIn = $signleIoArr['C/In'];
}
if(!isset($signleIoArr['C/Out']) || empty($signleIoArr['C/Out'])) {
$timeOut = '';
} else {
$timeOut = $signleIoArr['C/Out'];
}
$resultCsvLineArr['timeIn'] = $timeIn;
$resultCsvLineArr['timeOut'] = $timeOut;
$resultCsvArr[] = $resultCsvLineArr;
}
}
echo "--- Array Result ---\n";
var_export($resultCsvArr);
//get header line as arry of column names
//from the keys of the first array
$head = array_keys($resultCsvArr[0]);
//put arr of header as 1st sting to csv string
$csvStr = implode(',',$head) . "\n";
//put all the rest in result sting
foreach($resultCsvArr as $resultEntryArr) {
$csvStr .= implode(',',$resultEntryArr) . "\n";
}
// file_put_contents('path-to-file', $csvStr);
echo "\n\n --- CSV Result ---\n";
print_r($csvStr);
gives output:
--- Array Result ---
array (
0 =>
array (
'Name' => '"Soriano, Jhoniel"',
'EmpID' => 901,
'Date' => '05/03/2019',
'timeIn' => '',
'timeOut' => '1:01:03',
),
1 =>
array (
'Name' => '"Soriano, Jhoniel"',
'EmpID' => 901,
'Date' => '05/04/2019',
'timeIn' => '2:01:03',
'timeOut' => '3:01:03',
),
2 =>
array (
'Name' => '"Soriano, Jhoniel"',
'EmpID' => 901,
'Date' => '05/06/2019',
'timeIn' => '4:01:03',
'timeOut' => '5:01:03',
),
3 =>
array (
'Name' => '"Soriano, Jhoniel"',
'EmpID' => 901,
'Date' => '05/07/2019',
'timeIn' => '6:01:03',
'timeOut' => '',
),
)
--- CSV Result ---
Name,EmpID,Date,timeIn,timeOut
"Soriano, Jhoniel",901,05/03/2019,,1:01:03
"Soriano, Jhoniel",901,05/04/2019,2:01:03,3:01:03
"Soriano, Jhoniel",901,05/06/2019,4:01:03,5:01:03
"Soriano, Jhoniel",901,05/07/2019,6:01:03,
use:
file_put_contents('path-to-file', $csvStr);
at the end to save the csv to file.

Wordpress search through meta_value thats an array

If i use an array for a meta value can i see if a value is in the array when querying it? I have a website that has events that has dates attached to it as a meta value and i need to see if an event is on a certain date through a search.
$dates[] = 05/02/2016
$dates[] = 06/02/2016
$dates[] = 06/02/2016
update_post_meta($event, 'show_dates', $dates);
if i add this to an event how could i check if the 'show_dates' contains a date searched for? below is what i have tried already
$wp_query->set('post_status', array('publish', 'future'));
$wp_query->set("meta_key", "show_dates");
$wp_query->set("orderby", "meta_value");
$wp_query->set("order", "ASC");
$startDate = parseDatePicker($_GET['StartDate'], new \DateTime());
if (!is_null($startDate)) {
$wp_query->set("meta_query", array(
array(
'key' => "show_dates",
'value' => $startDate->format("d/m/Y"),
'compare' => 'IN'
)
));
}
Ok so it turns out the answer to this was easier than i expected. As Wordpress serializes the data in the array you can use LIKE instead of IN which will check the serialized array to see if it contains that date.
$wp_query->set('post_status', array('publish', 'future'));
$wp_query->set("meta_key", "show_dates");
$wp_query->set("orderby", "meta_value");
$wp_query->set("order", "ASC");
$startDate = parseDatePicker($_GET['StartDate'], new \DateTime());
if (!is_null($startDate)) {
$wp_query->set("meta_query", array(
array(
'key' => "show_dates",
'value' => $startDate->format("d/m/Y"),
'compare' => 'LIKE'
)
));
}
my next problem was how to work with a range I managed to figure this out and have posted below incase nayone else has a similar problem
if (!is_null($startDate) && !is_null($endDate) && ($startDate->format('d/m/Y') != $endDate->format('d/m/Y'))) {
$wp_query->set("relation", "OR");
$interval = DateInterval::createFromDateString('1 day');
$period = new DatePeriod($startDate, $interval, $endDate);
$dates[] = $startDate->format('d/m/Y');
foreach($period as $day){
$dates[] = array(
'key' => "next_showing_date",
'value' => $day->format('d/m/Y'),
'compare' => 'LIKE',
);
}
$wp_query->set("meta_query", $dates);
}

array_push creates new arrays instead of adding

I would like to add a key=>value pair to an existing array depending on an if statement.
But it keeps adding the key=>value pair as a new index.
Here is my code:
foreach ($messageRepo as $oneMessage) {
$calculatedTime = $oneMessage->getTimeToLive();
$creationTime = $oneMessage->getCrdate();
$calculatedTimes = $this->androidService->renderFormat($calculatedTime);
$expiringDate = $creationTime + $calculatedTime;
$ausgabe[] = array(
'Time' => key($calculatedTimes),
'Format' => current($calculatedTimes),
'Title' => $oneMessage->getMessageTitle(),
'Text' => $oneMessage->getMessageText(),
);
if (time() > $expiringDate) {
array_push($ausgabe[]['Expired'], $expiringDate);
} else {
array_push($ausgabe[]['Expiring'], $expiringDate);
}
}
The dump says:
array(60 items)
0 => array(4 items)
Time => 0 (integer)
Format => 'Stunden' (7 chars)
Title => '3 wochen total neu' (18 chars)
Text => 'dfdsfsdf fgdsfgdsgf' (19 chars)
1 => array(1 item)
Expired => NULL
But I want Expired => NULL as field in the original index and not as a new one.
You shouldn't use array_push in this case. Use simple assignment instead. As you don't know the index of the element that you are adding, you can create the new array, set all its values and then add it to the overall array. Something like this:
foreach ($messageRepo as $oneMessage) {
$calculatedTime = $oneMessage->getTimeToLive();
$creationTime = $oneMessage->getCrdate();
$calculatedTimes = $this->androidService->renderFormat($calculatedTime);
$expiringDate = $creationTime + $calculatedTime;
$newval = array(
'Time' => key($calculatedTimes),
'Format' => current($calculatedTimes),
'Title' => $oneMessage->getMessageTitle(),
'Text' => $oneMessage->getMessageText(),
);
if (time() > $expiringDate) {
$newval['Expired'] = $expiringDate;
} else {
$newval['Expiring'] = $expiringDate;
}
$ausgabe[] = $newval;
}
You are using array_push together with the $array[] notation, which does the same thing. The end result is creating a new element and then treating that as an array and putting another new element inside.
You should never have any need to use array_push directly. You should use something like this:
// This is what the new array inside $ausgabe will look like
$newItem = array(
'Time' => key($calculatedTimes),
'Format' => current($calculatedTimes),
'Title' => $oneMessage->getMessageTitle(),
'Text' => $oneMessage->getMessageText(),
);
if (...) {
// conditionally add more elements
$newItem['Expired'] = $expiringDate;
}
// Push the final result into $ausgabe
$ausgabe[] = $newItem;
Inserting the half-baked new array into $ausgabe gives trouble because when you want to add more sub-elements later you don't know the key that refers to the new array. You could find it out dynamically, but that's just too much trouble for no benefit.
Edit your code to:
$i=0;
foreach ($messageRepo as $oneMessage) {
$calculatedTime = $oneMessage->getTimeToLive();
$creationTime = $oneMessage->getCrdate();
$calculatedTimes = $this->androidService->renderFormat($calculatedTime);
$expiringDate = $creationTime + $calculatedTime;
$ausgabe[$i] = array(
'Time' => key($calculatedTimes),
'Format' => current($calculatedTimes),
'Title' => $oneMessage->getMessageTitle(),
'Text' => $oneMessage->getMessageText(),
);
if (time() > $expiringDate) {
$ausgabe[$i]['Expired'] = $expiringDate;
} else {
$ausgabe[$i]['Expired'] = $expiringDate;
}
$i++;
}

Categories