I'm trying to separate the dates for example:
$arr=array(
"2018-06-27 20:30:20",
"2018-06-27 20:31:20",
"2018-06-27 20:37:20",
"2018-06-27 20:45:20",
"2018-06-27 20:48:20"
);
As you can see there are minutes with difference only of 1 minute or even seconds.
What I'm trying to accomplish is to force the dates to be 5 mins interval.
example output
2018-06-27 20:30:00
2018-06-27 20:35:00
2018-06-27 20:40:00
2018-06-27 20:45:00
2018-06-27 20:50:00
Here's my code
function roundToNearestMinuteInterval($time)
{
$time = (round(strtotime($time) / 300)) * 300;
return date('Y-m-d H:i:s', $time);
}
$temp="";
$wave=1;
foreach($arr as $a) {
if(empty($temp)) {
$temp= roundToNearestMinuteInterval($a);
}
$date= roundToNearestMinuteInterval($a);
if($temp==$date && $wave!=1){
$new=date('Y-m-d H:i:s',strtotime('+3 minutes',strtotime($a)));
$date= roundToNearestMinuteInterval($date);
$temp= $date;
}
$wave++;
echo $date."<br/>";
}
If you want to have an output array with all the 5 minute (or other interval) times between the earliest and latest times in the input array, you can just iterate between them, adding the interval in each loop:
$arr=array("2018-06-27 20:30:20","2018-06-27 20:31:20","2018-06-27 20:37:20","2018-06-27 20:45:20","2018-06-27 20:48:20");
function roundToNearestMinuteInterval($time, $interval) {
$timestamp = strtotime($time);
$rounded = round($timestamp / ($interval * 60), 0) * $interval * 60;
return $rounded;
}
$interval = 5; // minutes
$start = roundToNearestMinuteInterval(min($arr), $interval);
$end = roundToNearestMinuteInterval(max($arr), $interval);
for (; $start <= $end; $start += $interval * 60) {
$results[] = date('Y-m-d H:i:s', $start);
}
print_r($results);
Output:
Array
(
[0] => 2018-06-27 20:30:00
[1] => 2018-06-27 20:35:00
[2] => 2018-06-27 20:40:00
[3] => 2018-06-27 20:45:00
[4] => 2018-06-27 20:50:00
)
Demo on 3v4l.org
Solution with a DateTime extension dt (https://github.com/jspit-de/dt) returns an array with the date as a key. The value supplied is the number of rounded values from the input array. The algorithm can be implemented even without class extension with a few more commands.
$inputArr = array(
"2018-06-27 20:30:20",
"2018-06-27 20:31:20",
"2018-06-27 20:37:20",
"2018-06-27 20:45:20",
"2018-06-27 20:48:20"
);
$interval = "5 Minutes";
//create basis
$resArr = [];
$dt = dt::create(min($inputArr))->round($interval); //start
$endDate = dt::create(max($inputArr))->round($interval);
for(;$dt <= $endDate; $dt->modify($interval)){
$key = $dt->format("Y-m-d H:i:s");
$resArr[$key] = 0;
}
foreach($inputArr as $strDate){
$key = $dt::create($strDate)
->round($interval)
->format("Y-m-d H:i:s");
$resArr[$key]++;
}
The result $resArr
array (
'2018-06-27 20:30:00' => 2,
'2018-06-27 20:35:00' => 1,
'2018-06-27 20:40:00' => 0,
'2018-06-27 20:45:00' => 1,
'2018-06-27 20:50:00' => 1,
)
Related
There is a large array with timestamps, e.g.:
$timestamps = array();
for ($i = 0; $i < 5000; $i++) {
$timestamps[] = mt_rand(strtotime('1900-01-01 00:00:00 am'), strtotime('2100-12-31 11:59:59 pm'));
}
Now I need to return the timestamps with the earliest (min) and latest (max) clock time.
My approach:
echo date('Y-m-d h:i:s a', min(array_map('callback', $timestamps)));
echo "\n";
echo date('Y-m-d h:i:s a', max(array_map('callback', $timestamps)));
function callback($timestamp) {
return strtotime(date('h:i:s a', $timestamp));
}
This actually provides the earliest and latest clock time, but of course along with the current date (today).
How to return the original timestamps with the earliest and latest clock time?
You can use next code:
//run array_reduce over array
$res = array_reduce(
$timestamps, // timestaps array
function($res, $t) {
// get time from timestamp
$time = date('H:i:s', $t);
// if result min not exists
// or more then $time store new value to $res['min']
if (is_null($res['min'][0]) || $time<$res['min'][0])
$res['min'] = [$time, date('Y-m-d h:i:s a', $t)];
// if result max not exists
// or less then $time store new value to $res['max']
if (is_null($res['max'][0]) || $time>$res['max'][0])
$res['max'] = [$time, date('Y-m-d h:i:s a', $t)];
// return updated result
return $res;
},
// define initial $res with null values
['min'=>[null, null], 'max'=>[null, null]]
);
Share PHP online
Result:
Array
(
[min] => Array
(
[0] => 00:00:30
[1] => 1997-05-03 12:00:30 am
)
[max] => Array
(
[0] => 23:59:36
[1] => 1983-07-21 11:59:36 pm
)
)
I am creating a function where you can give a starting year, month and end year, month and the function will print every month and year in between the two given points.
I've already created a function that works perfectly but I believe this is not a good practice and there might be better way to do this. Now I am seeking your help to find a better way.
P.S. I can't get full date as input. Only can get month number and year.
Here is my code -
function get_all_months($monthstart = null, $yearstart = null, $monthend = null, $yearend = null) {
if (($monthstart === null) || ($yearstart === null) || ($monthend === null) || ($yearend === null)) {
$monthend = date('m');
$yearend = date('Y');
if($monthend < 6) {
$yearstart = $yearend - 1;
$monthstart = (($monthend - 5) + 12);
} else {
$yearstart = $yearend;
$monthstart = $monthend - 5;
}
}
$month_array = array();
if ($yearstart > $yearend) {
for ($m=$monthend; $m<=12; $m++) $month_array[] = array('month' => $m, 'year' => $yearend);
for ($y=$yearend+1; $y<$yearstart; $y++) for ($m=1; $m<=12; $m++) $month_array[] = array('month' => $m, 'year' => $y);
for ($m=1; $m<=$monthstart; $m++) $month_array[] = array('month' => $m, 'year' => $yearstart);
} elseif ($yearend > $yearstart) {
for ($m=$monthstart; $m<=12; $m++) $month_array[] = array('month' => $m, 'year' => $yearstart);
for ($y=$yearstart+1; $y<$yearend; $y++) for ($m=1; $m<=12; $m++) $month_array[] = array('month' => $m, 'year' => $y);
for ($m=1; $m<=$monthend; $m++) $month_array[] = array('month' => $m, 'year' => $yearend);
} else {
for ($m=$monthstart; $m<=$monthend; $m++) $month_array[] = array('month' => $m, 'year' => $yearstart);
}
return $month_array;
}
EDIT: Based on Nigel Ren's answer, this is the best way I could think of -
function get_all_months($monthstart = null, $yearstart = null, $monthend = null, $yearend = null) {
if (($monthstart === null) || ($yearstart === null) || ($monthend === null) || ($yearend === null)) {
$monthend = date('m');
$yearend = date('Y');
if($monthend < 6) {
$yearstart = $yearend - 1;
$monthstart = (($monthend - 5) + 12);
} else {
$yearstart = $yearend;
$monthstart = $monthend - 5;
}
}
$output = [];
if ($yearstart > $yearend) {
$time = strtotime($yearend."-".$monthend);
$last = date('m-Y', strtotime($yearstart."-".$monthstart));
} else {
$time = strtotime($yearstart."-".$monthstart);
$last = date('m-Y', strtotime($yearend."-".$monthend));
}
do {
$cur_month_year = date('m-Y', $time);
$month = date('m', $time);
$year = date('Y', $time);
$output[] = array('month'=>$month,'year'=>$year);
$time = strtotime('+1 month', $time);
}
while ($cur_month_year != $last);
return $output;
}
You can use PHP's DateInterval class which is used for these purposes. Try this code
<?php
function getMonthsFromRange($start, $end, $format = 'M Y')
{
$array = array();
// Since you wanted 1 month it is Period = 1 Month
$interval = new DateInterval('P1M');
$realEnd = new DateTime($end);
$realEnd->add($interval);
$period = new DatePeriod(new DateTime($start), $interval, $realEnd);
// Use loop to store date into array
foreach ($period as $date)
$array[] = $date->format($format);
// Return the array elements
return $array;
}
// Function call with passing the start date and end date
$months = getMonthsFromRange('2010-10', '2011-11');
print_r($months);
It's output is:
Array ( [0] => Oct 2010 [1] => Nov 2010 [2] => Dec 2010 [3] => Jan 2011 [4] => Feb 2011 [5] => Mar 2011 [6] => Apr 2011 [7] => May 2011 [8] => Jun 2011 [9] => Jul 2011 [10] => Aug 2011 [11] => Sep 2011 [12] => Oct 2011 [13] => Nov 2011 )
Based on the answer here, it can be done simply by adding 1 month to the start date till you get to the end date...
function get_all_months($monthstart = null, $yearstart = null, $monthend = null, $yearend = null) {
$output = [];
$time = strtotime($yearstart."-".$monthstart);
$last = date('m-Y', strtotime($yearend."-".$monthend));
do {
$month = date('m-Y', $time);
$output[] = $month;
$time = strtotime('+1 month', $time);
}
while ($month != $last);
return $output;
}
So
print_r(get_all_months(4,2008,2,2010));
gives...
Array
(
[0] => 04-2008
[1] => 05-2008
[2] => 06-2008
[3] => 07-2008
[4] => 08-2008
[5] => 09-2008
[6] => 10-2008
[7] => 11-2008
[8] => 12-2008
[9] => 01-2009
[10] => 02-2009
[11] => 03-2009
[12] => 04-2009
[13] => 05-2009
[14] => 06-2009
[15] => 07-2009
[16] => 08-2009
[17] => 09-2009
[18] => 10-2009
[19] => 11-2009
[20] => 12-2009
[21] => 01-2010
[22] => 02-2010
)
probably you're looking for something like this:
function get_all_months($monthstart = null, $yearstart = null, $monthend = null, $yearend = null) {
$month_array = [];
if ($yearstart == $yearend)
for ($m=$monthstart; $m<=$monthend; $m++) $month_array[] = array('month' => $m, 'year' => $yearstart);
else {
for ($m=$monthstart; $m<=12; $m++) $month_array[] = array('month' => $m, 'year' => $yearstart);
for ($y=$yearstart+1; $y<$yearend; $y++) for ($m=1; $m<=12; $m++) $month_array[] = array('month' => $m, 'year' => $y);
for ($m=1; $m<=$monthend; $m++) $month_array[] = array('month' => $m, 'year' => $yearend);
}
return $month_array;
}
If I understand correctly - you get the month as a number and the year as a number e.g 6 and 1997. If we assume that the start year is always less than the end year I would suggest going like this.
function distanceBetweenDates(int $sm, int $sy, int $em, int $ey) {
$monthsBetweenYears = ($ey - $sy + 1) * 12;
$distanceBetweenMonths = $monthsBetweenYears - $sm - (12 - $em);
$startMonth = $sm + 1;
$startYear = $sy;
while ($distanceBetweenMonths > 0) {
if ($startMonth <= 12) {
echo $startMonth . ' - ' . $startYear;
} else {
$startMonth = 1;
$startYear++;
echo $startMonth . ' - ' . $startYear;
}
echo "\n";
$startMonth++;
$distanceBetweenMonths--;
}
}
The only thing you might need to see is if the given months are included or excluded from the calculation.
The only thing missing here is the validation since it can be "skipped" of a sort if you use types inside the method.
With the DateTime class and DateInterval, you can create a generator of DateTime object of the specified range:
<?php
function get_all_months(int $monthstart, int $yearstart, int $monthend, int $yearend) {
$onemonth = new DateInterval('P1M'); // one month interval
$timeperiod = new DatePeriod(
DateTime::createFromFormat('Y-m', "{$yearstart}-{$monthstart}"),
$onemonth,
DateTime::createFromFormat('Y-m', "{$yearend}-{$monthend}")->add($onemonth)
);
foreach ($timeperiod as $pos) {
yield clone $pos;
}
}
foreach (get_all_months(12, 2009, 1, 2020) as $month) {
echo "{$month->format('Y-m')}\n";
}
DateTime should be more flexible to use than plain string or number array (see DateTime::format for more usage options).
Note: inspired by #keidakida, updated my answer.
Situation:
I have arrays with information of the used appointments and the calculated days for the new appointment series based on PHP.
For example, the exist appointment array:
Array
(
[0] => Array
(
[date] => 2019-05-02
[time_start] => 14:00:00
[time_end] => 15:30:00
)
[1] => Array
(
[date] => 2019-05-06
[time_start] => 14:00:00
[time_end] => 15:30:00
)
)
Now, i will check have the calculated series (same array format) collisions with the exist appointments.
My Question:
How can i check if a collision exist beetween the start and end time and if yes how i can give the array a new value with a time windows after or before the exist appointment. This within a time windows from 08:00 am to 4:00 pm.
What i have is the calculation of the appointment days.
private function calculate_dates($data, $measure)
{
$this->load->model('calendar/calendar_model');
$holiday_dates = $this->calendar_model->get_holidays();
foreach ($holiday_dates as $key => $value) {
$holidays[] = $value['holiday_date'];
}
$begin = new DateTime($data->measure_begin);
$end = new DateTime($data->measure_end);
$oneday = new DateInterval("P1D");
$days = json_decode($data->measure_dates);
$wdays = array();
$ue_minutes = 0;
$minutes = ($data->measure_ue * $measure->ue_minutes/2);
$daterange = new DatePeriod( $begin, DateInterval::createFromDateString('+1 weekdays') ,$end );
foreach(new DatePeriod($begin, $oneday, $end->add($oneday)) as $day) {
$day_num = $day->format("N"); /* 'N' number days 1 (mon) to 7 (sun) */
if($day_num < 6 ) { /* weekday */
$wdays[] = $day;
}
}
$count = 1;
foreach($wdays as $date){
foreach ($days as $key => $value) {
if(mb_strtolower($date->format('l')) == $value){
if(($data->measure_ue/2)+1 != $count){
if(in_array($date->format('Y-m-d'), $holidays)) {
$dates[] = $this->close_days($date, $days, true)->format('l, d.m.Y');
} else {
$dates[] = $date->format('l, d.m.Y');
}
$count++;
}
}
}
}
return array(
'dates' => $dates,
'minutes' => round($minutes/count($dates))
);
}
private function close_days($date, $days, $init = false)
{
if($init){
$days[] = 'saturday';
$days[] = 'sunday';
}
if( in_array(mb_strtolower($date->format('l')), $days) ) {
$this->close_days($date->modify('next day'), $days);
}
return $date;
}
Any Ideas for a solution or maybe a code for a better way?
I am trying to Write Program for Calculating Next 20 dates After Specifying Start date, then from 20 dates i have Exclude Weekends & Holidays(Array holidays('2016-12-13',2016-12-24)) And Result Array which includes only Working Days Excluding Saturday & Sunday, from this Result Array after Passing Holiday array(Eg:- holidays('2016-12-13',2016-12-24))), it must be Excluded from result array. i:e;
I want Expected Output Below mentioned
.
<?php
$Date=array('2016-12-01');
echo "\n <br />Start Date:-" . $Date[0] . "";
/*Code For Generating Next 20 Dates Starts*/
//$start = strtotime($s_row['schedule_start_date']);
$start = strtotime('2016-12-01');
$dates=array();
for($i = 0; $i<20; $i++)
{
array_push($dates,date('Y-m-d', strtotime("+$i day", $start)));
}
echo "\n <br /> Array Of next 20 Days/dates of Given:-";
print_r($dates);
$start=array();
$start=$dates; /*Code For Generating Next 20 Dates Ends*/
$result=array();
$start = strtotime(array_values($Date)[0]);
//$end = strtotime(array_values($Date)[30]);
$result = array();
$begin = new DateTime( '2016-12-01' );
$end = new DateTime( '' );
//$end = $end->modify( '+1 day' );
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);
foreach($daterange as $date)
{
//echo $date->format("Y-m-d") . "<br>";
if (date('N', $start) <= 5) /* 'N' number days 1 (mon) to 7 (sun) */
/*5 weekday */
{
$current = date('Y-m-d', $start); //m/d/Y
$result[$current] = '';
}
$start += 86400;
//echo "Days Without Sat Sun".$result[date($date->format("Y-m-d"))];
//echo "Days Without Sat Sun".$result2[date($current->format("Y-m-d"))];
}
echo " \n <br /> Dates Without Weekends LIKE (Excluding Saturday & Sunday):-";
print_r($result);
/*For Holiday*/
$FinalArray = array();
$holidays = array(
'2016-12-13',
'2016-12-24',
);
echo " \n <br /> Given Holiday Dates Are:-";
print_r($holidays);
$a1 = $result;
$a2 = $holidays;
$array = array_diff(array_merge($a1,$a2),array_intersect($a1,$a2));
echo "\n <br /> Output:-";
print_r($array);
?>
it Gives Output as :- Array ( [2016-12-01] => [2016-12-02] => [2016-12-05] => [2016-12-06] => [2016-12-07] => [2016-12-08] => [2016-12-09] => [2016-12-12] => [2016-12-13] => [2016-12-14] => [2016-12-15] => [2016-12-16] => [2016-12-19] => [2016-12-20] => [2016-12-21] => [2016-12-22] => [2016-12-23] => [0] => 2016-12-13 [1] => 2016-12-24 )
> But I Want Expected Output:-
Array ( [2016-12-01] => [2016-12-02] => [2016-12-05] => [2016-12-06] => [2016-12-07] => [2016-12-08] => [2016-12-09] => [2016-12-12] => [2016-12-14] => [2016-12-15] => [2016-12-16] => [2016-12-19] => [2016-12-20] => [2016-12-21] => [2016-12-22] => [2016-12-23]
You Can Notice That 2016-12-13 is Not There in Above Expected Output as in '2016-12-13', 2016-12-24 is passed as Holiday via holiday array ($holidays = array( '2016-12-13', '2016-12-24', );) i:e; if i pass any date through holidays array it should not be included in result Array(). i:e 2016-12-13 is Available in Result array as well as holiday array So While while printing Final OUTPUT:- 13th date(2016-12-13) Should not be Included in final Output. Anybody Solve this will be Appreciated Thanks in Advance.
When I have to remove duplicates from a array the function that I keep going back to is
array array_unique ( array $array [, int $sort_flags = SORT_STRING ] )
you can find the documentation Here
<?php
$input = array("a" => "green", "red", "b" => "green", "blue", "red");
$result = array_unique($input);
print_r($result);
?>
the output
Array
(
[a] => green
[0] => red
[1] => blue
)
I hope that this was able to help
I prefer to calculate all dates just in one pass. (You may skip filling $dates and $dates_mon_fri arrays if they doesn't used in output also.) There is yet another approach to avoid array_diff() and array_unique() functions. I've used an array_flip() to exchange keys with values in $holdidays array to use fast array_key_exists() function.
<?php
$start = strtotime('2016-12-01');
$holidays = [
'2016-12-13',
'2016-12-24',
];
$dates = [];
$dates_mon_fri = [];
$dates_working = [];
$flip_holidays = array_flip($holidays);
for ($i = 0; $i < 20; $i++) {
$timestamp = strtotime("+$i day", $start);
$date = date('Y-m-d', $timestamp);
$dates[] = $date;
$mon_fri = false;
if (date('N', $timestamp) <= 5) {
$dates_mon_fri[] = $date;
$mon_fri = true;
}
if ($mon_fri && !array_key_exists($date, $flip_holidays)) {
$dates_working[] = $date;
}
}
var_dump($dates);
var_dump($dates_mon_fri);
var_dump($dates_working);
You can avoid using explicit looping:
$begin = new DateTimeImmutable('2016-12-01');
$end = $begin->modify('+20 days');
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval, $end);
$allDates = iterator_to_array($daterange);
$datesExcludingWeekends = array_filter($allDates, function ($date) {
return (int) $date->format("N") < 6;
});
$datesExcludingWeekends = array_map(
'date_format',
$datesExcludingWeekends,
array_fill(1, count($datesExcludingWeekends), 'Y-m-d')
);
$holidays = [
'2016-12-13',
'2016-12-24',
];
$datesExcludingWeekendsIncludingHolidays = array_flip(array_merge(
$datesExcludingWeekends,
array_diff($holidays, $datesExcludingWeekends)
));
Here is working demo.
Also, take a look at the Carbon library. If you need some exhaustive working with dates this library can really ease your life.
What is the most efficient way to get an array of months, from a specified date, up until the present day, grouped by year.
Eg getMonths("August 2012") would output
array(
array("Year"=>"2013", "months" = array(
"February", "January")
),
array("Year"=>"2012", "months" = array(
"December", "November","October", "September", "August")
)
)
So far I've got:
$start = strtotime('2012-08-01');
$end = time();
$month = $start;
$months[] = date('F', $start);
while($month <= $end) {
$month = strtotime("+1 month", $month);
$months[] = date('F', $month);
}
This is outputting the correct months, but not grouping them into years.
Thanks
You can try
function getMonths($month,$count = 1) {
$now = new DateTime();
$start = DateTime::createFromFormat("F Y", $month);
$list = array();
$interval = new DateInterval(sprintf("P%dM",$count));
while ( $start <= $now ) {
$list[$start->format("Y")][] = $start->format("F");
$start->add($interval);
}
return $list;
}
print_r(getMonths("August 2012"));
Output
Array
(
[2012] => Array
(
[0] => August
[1] => September
[2] => October
[3] => November
[4] => December
)
[2013] => Array
(
[0] => January
[1] => February
)
)
Since the answer posted here did not work for me (also tried online sandbox to be sure) i wrote a method that works with the very most versions of PHP:
function getMonths($monat, $year) {
$list = array();
for(;$monat <= 12;$monat++) {
if($year == date("Y") && $monat == date("m")) { // exit on current month+year
break;
}
if(!isset($list[ $year ])) {
$list[ $year ] = array();
}
$list[ $year ][ str_pad($monat, 2, '0', STR_PAD_LEFT) ] = date("F", strtotime('01.' . $monat . '.' . $year));
if($monat == 12) {
$monat = 0;
$year++;
}
}
return $list;
}