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.
Related
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
In my application I have duplicated code include foreach. I have a foreach where I only store the value from $schedules to new array. Then I used another foreach to check if this array is in new $array.
I have an array here $schedules:
$schedules = [
[
"time_start" => "08:00:00",
"time_end" => "12:00:00",
"in_threshold" => 50,
"out_threshold" => 15
],
[
"time_start" => "13:00:00",
"time_end" => "17:00:00",
"in_threshold" => 30,
"out_threshold" => 45
]
];
So I have this code snippet using forloop to store to new array:
foreach($schedules as $schedule){
// Set time_start and time_end to array
$sched_starts[] = ( $schedule[ 'time_start' ] );
$sched_ends[] = ( $schedule[ 'time_end' ] );
// Set time_start and time_end with threshold to array
$time_start[] = date( "H:i:s",
strtotime('+'. $schedule[ 'in_threshold' ] .' minutes',
strtotime ( $schedule[ 'time_start' ]
)));
$time_end[] = date( "H:i:s",
strtotime('+'. $schedule[ 'out_threshold' ] .' minutes',
strtotime( $schedule[ 'time_end' ]
)));
}
I want to get $sched_starts,$sched_ends,$time_start, and $time_end without using a loop to avoid duplicated loops. So I used array_column and array_walk_recursive function but I can't get the $time_start and $time_end then I realized the two functions only get single column in the $schedules at a time.
Is it possible not to use the loop in getting the $time_start and $time_end?
You can use end , which will point to the last element
$c = end($schedules);
$time_start = $c['time_start'];
$time_end = $c['time_end'];
How can I get the Financial Year date range in PHP like below when I pass year and return date range of every year start and end.
Like Eg.
Input Array = [2017,2018]
Financial Start Month = 04
Output Array =
[
'2017' => [
'start' => '2016-04-01',
'end' => '2017-03-31'
],
'2018' => [
'start' => '2017-04-01',
'end' => '2018-03-31'
]
]
My Effort:-
$year_arr = [2017,2018];
$fn_month = 04;
$date_range_arr = [];
foreach ($year_arr as $key => $value) {
$fn_start_date_year = ($value - 1);
$fn_start_date_month = $fn_month;
$fn_start_date_day = '01';
$fn_start_date_string = $fn_start_date_year.'-'.$fn_start_date_month.'-'.$fn_start_date_day;
$start_date = date('Y-m-d',strtotime($fn_start_date_string));
$fn_end_date_year = ($value);
$fn_end_date_month = (fn_month == 1)?12:(fn_month-1);
$fn_end_date_day = date('t',strtotime($fn_end_date_year.'-'.$fn_end_date_month.'-01'));
$fn_start_date_string = $fn_end_date_year.'-'.$fn_end_date_month.'-'.$fn_end_date_day;
$end_date = date('Y-m-d',strtotime($fn_start_date_string));
$date_range_arr[$value] = [
'start_date' => $start_date,
'end_date' => $end_date
];
}
Above is my effort. It is working perfectly but needs a more robust code.
A good way to manipulate dates in PHP is using the DateTime class. Here's an example of how to get the results you want using it. By using the modify method, we can avoid worries about complications like leap years (see the result for 2016 below).
$year_arr = [2016,2017,2018];
$fn_month = 03;
foreach ($year_arr as $year) {
$end_date = new DateTime($year . '-' . $fn_month . '-01');
$start_date = clone $end_date;
$start_date->modify('-1 year');
$end_date->modify('-1 day');
$date_range_arr[$year] = array('start_date' => $start_date->format('Y-m-d'),
'end_date' => $end_date->format('Y-m-d'));
}
print_r($date_range_arr);
Output:
Array (
[2016] => Array (
[start_date] => 2015-03-01
[end_date] => 2016-02-29
)
[2017] => Array (
[start_date] => 2016-03-01
[end_date] => 2017-02-28
)
[2018] => Array (
[start_date] => 2017-03-01
[end_date] => 2018-02-28
)
)
Demo on 3v4l.org
Maybe this is what you need?
I use strtotime to parse the date strings.
$year_arr = [2017,2018];
$fn_month = 04;
$date_range_arr = [];
foreach($year_arr as $year){
$date_range_arr[$year] =['start' => date("Y-m-d", strtotime($year-1 . "-" .$fn_month . "-01")),
'end' => date("Y-m-d", strtotime($year . "-" .$fn_month . "-01 - 1 day"))];
}
var_dump($date_range_arr);
Output:
array(2) {
[2017]=>
array(2) {
["start"]=>
string(10) "2016-04-01"
["end"]=>
string(10) "2017-03-31"
}
[2018]=>
array(2) {
["start"]=>
string(10) "2017-04-01"
["end"]=>
string(10) "2018-03-31"
}
}
https://3v4l.org/nMUHt
Try this snippet,
function pr($a)
{
echo "<pre>";
print_r($a);
echo "</pre>";
}
$year_arr = [2017, 2018];
$fn_month = 4;
$date_range_arr = [];
foreach ($year_arr as $key => $value) {
$fn_month = str_pad(intval($fn_month),2, 0, STR_PAD_LEFT);
$date = "".($value-1)."-$fn_month-01"; // first day of month
$date_range_arr[$value] = [
'start_date' => $date,
'end_date' => date("Y-m-t", strtotime($date.' 11 months')), // last month minus and last date of month
];
}
pr($date_range_arr);
die;
str_pad - Pad a string to a certain length with another string
Here is working demo.
I have a script that queries an API and finds records within the last day.
I'd like to then loop through these results and solely get those within the last hour.
Can someone explain how I do this?
This is my array of daily results:
array(2) {
[0]=>
array(36) {
["CallRef"]=> string(10) "1234567891"
["CallStartTime"]=> string(8) "08:18:30"
}
[1]=>
array(36) {
["CallRef"]=> string(10) "1234567892"
["CallStartTime"]=> string(8) "14:04:20"
}
}
It's 14:40 here in the UK so my script should just grab the 2nd item from the array.
How about this?
$apiElements = [
['CallRef' => '1234567891', 'CallStartTime' => '08:18:30'],
['CallRef' => '1234567892', 'CallStartTime' => '14:04:20'],
];
$currentFormatted = (new DateTime())->format('H');
$startOfHour = DateTime::createFromFormat('H:i:s', $currentFormatted . ':00:00');
$endOfHour = DateTime::createFromFormat('H:i:s', $currentFormatted . ':59:59');
$callsInHour = array_filter($apiElements, function($element) use ($startOfHour, $endOfHour) {
$dt = DateTime::createFromFormat('H:i:s', $element['CallStartTime']);
return $dt >= $startOfHour && $dt <= $endOfHour;
});
Totally untested, but give it a try.
Next time, please post what code you tried...
Theres multiple ways to do this. You can get a Unix timestmap from a hour ago: strtotime('-1 hour'). You can explode the time on every : in the time and take the 2nd result from the explode result.
foreach ($array as $value){
$exploded=explode(":",$value['CallStartTime']);
if(date('H')-1>$exploded[1]){
//This means it's an hour ago.
}
}
Replace $allResults and try this:
date_default_timezone_set('UTC');
$hourAgo = strtotime(date('H:i:s')) - 3600;
foreach ($allResults as $result){
if(strtotime($result["CallStartTime"]) > $hourAgo){
var_dump($result);
}
}
More flexible solution:
First you need a function to convert time to seconds
function convertTimeToSeconds($hhmmss)
{
//correct format if needed
list($hours, $minutes, $seconds) = explode(':', $hhmmss);
return $hours * 3600 + $minutes * 60 + $seconds;
}
And then you just can use array_filter function
$now = date('H:i:s');
$nowSeconds = convertTimeToSeconds($now);
$filterFunction = function ($value) use ($nowSeconds) {
return ($nowSeconds - convertTimeToSeconds($value['CallStartTime'])) < 3600; //3600 seconds = hour
};
$filteredList = array_filter($array, $filterFunction);
All together https://3v4l.org/icAmp
I suppose you have this array
Array
(
[0] => Array
(
[CallRef] => 1234567892
[CallStartTime] => 21:04:20
)
[1] => Array
(
[CallRef] => 1234567892
[CallStartTime] => 09:08:08
)
[2] => Array
(
[CallRef] => 1234567892
[CallStartTime] => 08:11:08
)
[3] => Array
(
[CallRef] => 1234567892
[CallStartTime] => 20:59:08
)
)
And so i tried this
<?php
$arr = array(
"0" => array (
"CallRef" => 1234567892,
"CallStartTime" => "21:04:20"
),
"1" => array (
"CallRef" => 1234567892,
"CallStartTime" => "09:08:08"
),
"2" => array (
"CallRef" => 1234567892,
"CallStartTime" => "08:11:08"
),
"3" => array (
"CallRef" => 1234567892,
"CallStartTime" => "20:59:08"
)
);
//echo "<pre>";print_r($arr);die;
date_default_timezone_set('Asia/kolkata');//set your timezone
$currentTime = date('H:i:s');//getting current time
$lastHourTime = date('H:i:s', strtotime('-1 hour'));//getting last hour time
$result = array();
foreach ($arr as $singlearr){
if(strtotime($singlearr['CallStartTime']) >= strtotime($lastHourTime) && strtotime($singlearr['CallStartTime']) <= strtotime($currentTime)){
$result[] = $singlearr;
}else{
}
}
echo "<pre>";print_r($result);die;//this gives me last hour records only
Check Demo When you check demo All records which have time > last hour will return otherwise it will return empty array
May be it can help!
I just wondered if anybody can point me in the right direction: I'm looking to make a script whereby the logo on my site changes depending on the date; so for instance a haloween style one soon.
I started off by having 2 arrays, 1 of start dates and 1 of end dates(not sure even if this is the best way!):
<?php
$start_dates = array('01/01' => 'New Years',
'14/02' => 'Valentine Day',
'16/02/2010' => 'Pancake Day',
'17/03' => 'St Patricks Day',
'01/04' => 'April Fools',
'02/04/2010' => 'Easter',
'23/04' => 'St Georges Day',
'11/06/2010' => 'World Cup',
'31/10' => 'Halloween',
'05/11' => 'Guy Fawkes',
'11/11' => 'Armistice Day',
'16/10' => 'Today',
'15/12' => 'Christmas');
$end_dates = array( '08/01' => 'New Years',
'15/02' => 'Valentine Day',
'17/02/2010' => 'Pancake Day',
'18/03' => 'St Patricks Day',
'02/04' => 'April Fools',
'06/04/2010' => 'Easter',
'24/04' => 'St Georges Day',
'12/07/2010' => 'World Cup',
'01/11' => 'Halloween',
'06/11' => 'Guy Fawkes',
'12/11' => 'Armistice Day',
'17/10' => 'Today',
'01/01' => 'Christmas');
?>
Easy so far...the problemis that I need a way of working out if todays date falls between the start date and end date, then changing the image file name.
Its a long shot but I hope someone would be kind enough to help.
Thanks,
B.
like this
$events = array(
'New Year' => '01/01 01/08',
'Pancake Day' => '16/02/2010 17/02/2010',
//etc
);
echo find_event($events, '16/02');
where find_event() is
function mdy2time($date) {
$e = explode('/', $date);
if(count($e) < 3)
$e[] = '2010';
return strtotime("$e[1]-$e[0]-$e[2]");
}
function find_event($events, $date = null) {
$date = is_null($date) ? time() : mdy2time($date);
foreach($events as $name => $range) {
list($start, $end) = explode(' ', $range);
if($date >= mdy2time($start) && $date <= mdy2time($end))
return $name;
}
return null;
}
you should use an array more like this:
$dates = array();
$dates[] = array(
'name' => 'New Years'
'start' = '01/14',
'end' => '01/20',
'style' => 'haloween',
);
$dates[] = array(
//...
);
then you can get the style as follows:
$style='default';
// date as number e.g. 130 (january 30th)
$currDate = date('md',time()) * 1;
foreach ($dates AS $k => $v) {
$tmp = explode("/",$v['start'];
$start = ($tmp[1].$tmp[0])*1;
$tmp = explode("/",$v['end'];
$stop = ($tmp[1].$tmp[0])*1;
if ($start <= $currDate && $currDate < $stop) {
$style=$v['style'];
break;
}
}
echo 'style: '.$style;
Didn't check the code yet, so feel free to correct me if iam wrong.