Need help sorting multi array - php

I see a lot of examples here, but it's not working for me.
I have a list of sorted events retrieved from my DB:
16.08.2010 12:00:00 - 21.08.2010 20:00:00
16.08.2010 20:00:00 - 21.08.2010 23:00:00
18.08.2010 17:00:00 - 18.08.2010 19:00:00
As you can see, the first event is from 16.08 to 21.08.
I need to "chop this up" so that I get one entry foreach day.
This is my function:
function fullList( $data ) {
$ev = $data['events'];
$events = array();
// Loop through each event. If event spans over several days, add each day in to new event list
foreach($ev as $e) :
$endDate = strtotime(date('Y-m-d 23:59:59', strtotime($e->endTime)));
$current = strtotime($e->startTime);
// Copy event so data is not overwritten
$newEv = $e;
while ($current <= $endDate) {
//Set start date of event to new date
$newEv->startTime = date('d.m.Y H:i:s', $current);
// Add events to new event list
array_push($events,$newEv);
//Go to next date
$current = strtotime('+1 day', $current);
}
endforeach;
// Need to sort $events here
}
Now $events contains all events, but it's not sorted by date. I've tried uasort, but I can't use uasort($array, 'cmp');.
How can I go about sorting this array by date?

Have you tried natural sorting algorithm?

As Nazariy mentioned, you would be better off pre-sorting the events at the source (assuming they come from a DB).
If you must sort the array in code, you should be able to use usort. I'm assuming you want to sort by startTime
usort($events, function($a, $b) {
return strtotime($a->startTime) - strtotime($b->startTime);
});

Related

Finding the average date given an array of PHP DateTime Objects

I have an array of PHP DateTime objects:
$dates = [
new DateTime('2019-08-15'),
new DateTime('2019-08-19'),
new DateTime('2019-08-20')
];
What I would like to receive from this array is the average date, which in my calculation would be 2019-08-18.
Is there a simple way of doing this without breaking down the date parts for each item and finding the average of all of them and then splicing it back together?
Thank you!
Basically you have no choice other than to iterate over all the values and summing them (using timestamps is the most efficient way), taking the average and then converting that value back to a date:
echo date('Y-m-d', array_reduce($dates, function ($c, $d) {
return $c + $d->format('U');
}, 0) / count($dates));
An alternate way would be to find the difference between each of the dates and the first date in the array, and then take the average of those values and add it to the first date:
$days = 0;
foreach ($dates as $date) {
$days += $dates[0]->diff($date)->days;
}
$days = intdiv($days, count($dates));
$avg_date = (clone $dates[0])->modify("+$days days");
echo $avg_date->format('Y-m-d');
In both cases the output is:
2019-08-18
Demo on 3v4l.org

Get only the next date from multiple dates

I have several dates, some of the past, some of the future, and I want to show only the next date in the future of this list. If there is no date in the future, I want to echo some text.
I am using ACF Date Picker inside a Repeater. First of all, I think I have to get the current date, to compare it with the others:
date_default_timezone_set('Europe/Berlin');
$date = date('Ymd', time());
I chose the Ymd format because the repeater outputs the dates the exact same way. Now I need to check somehow all subfields inside my repeater. I guess this is done using somehow like
$rows = get_field('repeater_field_name');
if($rows) {
foreach($rows as $row) {
// compare the dates, choose only the next coming date
// dates are stored in the_sub_field('repeater_date_field');
}
}
How can I get all the_sub_field('repeater_date_field') of my get_field('repeater_field_name') values? And how can I detect its row (for adding other sub fields to the same row)?
You can store your all dates in array. Then sort it in ascending order. Then take the first date from the list which is greater than today.
Please look into below code.
date_default_timezone_set('Europe/Berlin');
$date = date('Ymd', time());
$dates = array();
$rows = get_field('repeater_field_name');
if($rows) {
foreach($rows as $row) {
$dates[] = $row->date_field;
// compare the dates, choose only the next coming date
// dates are stored in the_sub_field('repeater_date_field');
}
}
sort($dates);
$final_date = "";
foreach($dates as $date_row){
if($date_row > $date){
$final_date = $date_row;
return true;
}
}
echo $final_date;
You can compare 2 date to solve this problem. For example
$date_bef = strtotime("151218"); // December 18, 2015
$date_after = strtotime("151219"); // December 19, 2015
if($date_after - $date_bef == 1)
echo "This is a next day";
If you don't want compare, you can add 1 day to existing time, example
$next_day = date("Ymd",strtotime("151220") + 60*60*24);

Codeigniter query to data from database for each day

I have a $from date and $to date with me so I want to fetch four values from the the database for days in between $from and $to and including $from and $to . If the data does not exist for a day in the database then zero must in placed as the value for the missing values.
So how would I write a query in codeigniter to make it happen and the corresponding date should be stored in the result for a particular row .
My previous solution is that I using PHP to check and set zero for missing date. But I have some new solutions and you can try
Create a table to store dates and left/right join with your table
Or create temporary table using stored procedure and join. Temporary will be auto deleted when a session is expired
Using UNION with select statement
There're many answers on StackOverflow
MySQL how to fill missing dates in range?
MySQL group by date and count including missing dates
Running total over date range - fill in the missing dates
MySQL to fill in missing dates when using GROUP BY DATE(table.timestamp) without joining on temporary table
i think you solution needs to be actually in PHP and not sure if you can get what you are looking for directly from MYSQL just by a query. As from what i understand you want to run a query, get all records that are in your defined date range and then have dates that has no records have an empty row (or with any other value you decide...).
I would actually run the same query you have for selecting rows between the daterange and use DatePeriod Class to generate an array of all days between the start and end dates.
$begin = new DateTime( '2012-08-01' );
$end = new DateTime( '2012-10-31' );
$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>";
}
With this we will be able to run over each day from the $from_date to the $end_date.
Next we will need to go other the rows from the DB and see on which days there are records and where no according the the daterange object we have.
Here is an approach that would work i believe, it's not the cleanest sample but some additional work on it and you can make it a bit prettier, but i think that will work for what you need.
The Database section in the code is not in Codeigniter but as it is only getting a simple query you should not have any trouble changing that.
// set the start & end dates
$from_date = '2012-09-11';
$to_date = '2012-11-11';
// create a daterange object
$begin = new DateTime($from_date);
$end = new DateTime($to_date );
$end = $end->modify( '+1 day' );
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);
$sth = $dbh->prepare("SELECT col1, dateCol from tb WHERE dateCol>'".$from_date."' AND dateCol<'".$to_date."' order by dateCol ");
$sth->execute();
$rows = $sth->fetchAll(PDO::FETCH_ASSOC);
$rowsByDay = array(); // will hold the rows with date keys
// loop on results to create thenew rowsByDay
foreach($rows as $key=>$row) {
$rowsByDay[strtotime($row['dateCol'])][] = $row; // add the row to the new rows array with a timestamp key
}
// loop of the daterange elements and fill the rows array
foreach($daterange as $date){
if(!isset($rowsByDay[strtotime($date->format("Y-m-d"))])) // if element does not exists - meaning no record for a specific day
{
$rowsByDay[strtotime($date->format("Y-m-d"))] = array(); // add an empty arra (or anything else)
}
}
// sort the rowsByDay array so they all are arrange by day from start day to end day
ksort($rowsByDay);
// just for showing what we get at the end for rowsByDay array
foreach ($rowsByDay as $k=>$v) {
echo date('Y-m-d',$k);
var_dump($v);
echo '<hr/>';
}
Hope this gets you on the right way...
Hope this helps,
$this->db->where('date_column >= ',$date_given_start);
$this->db->where('date_column <= ',$date_given_end);
$this->db->get('TABLE_NAME')->result();
// if column is datetime
$this->db->where('DATE(date_column) >= ',$date_given_start);
$this->db->where('DATE(date_column) <= ',$date_given_end);
$this->db->get('TABLE_NAME')->result();

php, get the the highest-closest time to current from array? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to get most recent date from an array of dates?
I have a timetable which is an array:
array(
'01.01.2012|11:00',
'01.01.2012|14:30',
'01.01.2012|16:24', // example match
'01.01.2012|17:20',
'01.01.2012|17:43',
'02.01.2012|10:20',
'02.01.2012|12:30',
); // etc.
I want a Cron job that will check current date/time and compare to ones in array. First I check if the date is a match, that's no problem. But then I need to find and display the earliest time from that array which is after the current time. If there's no suitable time within the same date then I display the earliest time from the next date.
For example: lets take the array above and current date/time of 01.01.2012|14:45
The date is a match so we continue
The next time from the array 16:24, but how to find it using PHP? And if the current time is higher than anything for the same date in array, then get the earliest time from next date?
Obviously to get the string with correct date I use "foreach" and "if" and it returns fine. But then how do I go through the times?
Convert to timestamp, sort and iteratively compare with current time.
$ts = array_map(
create_function('$a','return strtotime(str_replace("|", " ", $a));'),
$dates);
$len= count($ts); $now = time();
sort($ts);
for($i=0;$i<$len && (!($now<$ts[$i]));$i++);
echo date("d.m.Y|H:i",$ts[$i]);
Functions of Interest
array_map
create_function
str_replace
If you convert these to UNIX timestamps, you can sort them numerically, loop through and take the first item that is larger than the current timestamp.
You might consider converting those dates to UNIX timestamps:
function getUnixTimestamp($string) {
list($date, $time) = explode('|', $string);
return strtotime("$date $time");
}
Then you could use something like:
$array = array(); // from example
$timestamps = array_map('getUnixTimestamp', $array);
$current = time();
// create an array mapping timestamp to string
$keyedArray = array_combine($timestamps, $array);
// sort by timestamp
ksort($keyedArray);
foreach ($keyedArray as $timestamp => $string) {
if ($timestamp > $current) {
// this is the first timestamp after current time
}
}
You may want to do some extra checking on $timestamp, making sure it's either on the same or next day, but it's easier to work with timestmap comparisons than string matches.
Write a comparison function for the date-time format you have then loop through until you find a date greater or equal to your reference date.
function compareDateTime($dt1, $dt2) {
sscanf($dt1, "%d.%d.%d|%d:%d", $day, $month, $year, $hour, $minute);
$comp1 = $year . $month . $day . $hour . $minute;
sscanf($dt1, "%d.%d.%d|%d:%d", $day, $month, $year, $hour, $minute);
$comp2 = $year . $month . $day . $hour . $minute;
return $comp1 - $comp2;
}
Returns -ve when $dt1 < $dt2 and +ve when $dt1 > $dt2

Check for consecutive dates within a set and return as range

I have arrays of dates Y-m-d format that may be any combination of ten set dates that are one day apart.
e.g. Here is the full set:
2011-01-01, 2011-01-02, 2011-01-03, 2011-01-04, 2011-01-05, 2011-01-06, 2011-01-07, 2011-01-08, 2011-01-09, 2011-01-10
The arrays that are created from that set can be any combination of dates— all of them, one of them, some consecutive, all consecutive, etc.
I currently have them printing pretty much as they're returned. e.g. here's a possible result:
2011-01-02
2011-01-03
2011-01-04
2011-01-08
(what's actually printed is more like "Friday, Jan. 2…", but we'll stick with the simple date string)
I'd like to condense it so that if there are three or more consecutive days, those become a range, e.g the above example would become:
2011-01-02 to 2011-01-04
2011-01-08
which would eventually become:
Sunday, Jan. 2 - Tuesday, Jan. 4
Saturday Jan. 8
Is there a way to loop through and check the time difference, create a start time and end time for the range(s), and then gather up the stragglers?
Bit of a quick answer so sorry about the lack of implementation but assuming you are using 5.3 and the dates are ordered chronologically, you could convert each date to a DateTime object (if they aren't already) and then iterate over the array using DateTime::diff() to generate a DateInterval object which you could use to compare the current date in the iteration with the last. You could group your consecutive dates into sub arrays and use shift() and pop() to get the first and last days in that sub array.
EDIT
I had a think about this. Pretty rough and ready implementation follows, but it should work:
// assuming a chronologically
// ordered array of DateTime objects
$dates = array(
new DateTime('2010-12-30'),
new DateTime('2011-01-01'),
new DateTime('2011-01-02'),
new DateTime('2011-01-03'),
new DateTime('2011-01-06'),
new DateTime('2011-01-07'),
new DateTime('2011-01-10'),
);
// process the array
$lastDate = null;
$ranges = array();
$currentRange = array();
foreach ($dates as $date) {
if (null === $lastDate) {
$currentRange[] = $date;
} else {
// get the DateInterval object
$interval = $date->diff($lastDate);
// DateInterval has properties for
// days, weeks. months etc. You should
// implement some more robust conditions here to
// make sure all you're not getting false matches
// for diffs like a month and a day, a year and
// a day and so on...
if ($interval->days === 1) {
// add this date to the current range
$currentRange[] = $date;
} else {
// store the old range and start anew
$ranges[] = $currentRange;
$currentRange = array($date);
}
}
// end of iteration...
// this date is now the last date
$lastDate = $date;
}
// messy...
$ranges[] = $currentRange;
// print dates
foreach ($ranges as $range) {
// there'll always be one array element, so
// shift that off and create a string from the date object
$startDate = array_shift($range);
$str = sprintf('%s', $startDate->format('D j M'));
// if there are still elements in $range
// then this is a range. pop off the last
// element, do the same as above and concatenate
if (count($range)) {
$endDate = array_pop($range);
$str .= sprintf(' to %s', $endDate->format('D j M'));
}
echo "<p>$str</p>";
}
Outputs:
Thu 30 Dec
Sat 1 Jan to Mon 3 Jan
Thu 6 Jan to Fri 7 Jan
Mon 10 Jan

Categories