tags:

views:

301

answers:

3

If you have an array of ISO dates, how would you calculate the most days between two sequential dates from the array?

$array = array('2009-03-11', '2009-03-12', '2009-04-12', '2009-05-03', '2009-10-30');

I think I need a loop, some sort of iterating variable and a sort. I can't quite figure it out.

This is actually being output from MYSQL.

+1  A: 

EDIT:
As [originally] worded the question can be understood in [at least ;-)] two ways:

  • A) The array contains a list of dates in ascending order. The task is to find the longuest period (expressed in number of days) between to consecutive dates in the array.
  • B) The array is not necessarily sorted. The task is to find the longuest period (expr. in number of days) between any two dates in the array

The following provides an answer to the "B" understanding of the question. For a response to "A", see dcneiner's solution


No Sorting needed!...

If it comes from MySQL, you may have this DBMS returns directly the MIN and MAX values for the considered list.
EDIT: As indicated by Darkerstar, the way the way the data is structured [and also the existing SQL query which returns the complete list as indicated in the question] generally dictate the way the query which produces the MIN and MAX value should be structured.
Maybe something like this:

SELECT MIN(the_date_field), MAX(the_date_field)
FROM the_table
WHERE -- whatever where conditions if any
--Note: no GROUP BY needed

If, somehow, you cannot use SQL, a single pass through the list will allow you to obtain the MIN and MAX value in the list (in O(n) time, that is).
Algorithm is trivial:
Set Min and Max Value to first item in [unsorted] list.
Iterate through each following item in the list, comparing it with the Min Value and replacing it if found smaller, and doing like-wise for the Max value...

With Min and Max values in hand, a simple difference gives the max number of days...
In PHP, it's looks like the following:

<?php
$array = array('2009-03-11', '2009-03-12', '2009-04-12', '2009-05-03', '2009-10-30');

# may need this as suggested by dcneiner
date_default_timezone_set("GMT");

$max = $array[0];
$min = $max;
for($i = 1; $i < count($array); $i++){
    // Note that since the strings in the array are in the format YYYY-MM-DD,
    // they can be compared as-is without requiring say strtotime conversion.
    if ($array[$i] < $min)
        $min = $array[$i];
    if ($array[$i] > $max)
        $max = $array[$i];
}
$day_count = (strtotime($max) - strtotime($min)) / (60*60*24);

?>
mjv
MIN and MAX is not really relevant. The most days between dates is not necessarily MIN or MAX(date). I could do a DATEDIFF between every row, but I'm not really sure how you would write that either.
rrrfusco
Unless I misunderstand the problem, Min and Max are quite relevant. While your plan of comparing all possible pairs of dates from the array can work as well, this approach ends-up doing a lot more work. In addition to having to keep track of the min and max values ("so far"), in your case the min/max of the difference, but min/max all the same..., you need to perform n * (n - 1) differences, which makes the algorithm O(n^2), not to mention the overhead/complexity associated with managing the generation of all the pairs...
mjv
How you write the SQL depend on how the data is structured. If you have one date field, you can write like:"select TO_DAYS(MIN(mydate), MAX(mydate)) from mytable where cust_id=xx group by cust_id"If you are trying calculating two dates in one row, then:"select DATEDIFF(date1, date2) as numdays, MAX(numdays) from table where xxxx group by xxxx"
Darkerstar
I guess I lost you when you wrote MYSQL can output the min and max values. I didn't understand what the following step would be... (provided MYSQL could solve the problem without PHP)
rrrfusco
Last comment was for mjv :)
rrrfusco
@rrrfusco, see edits, providing both a PHP snippet based on the assumption that PHP needs to work from the complete list, as well as comment on the SQL query which could produce directly the Min and Max. MySQL could indeed produce the date difference in days but this would involve a subquery, [or a MAX(DATEFIFF...) on a self join which would be expensive processing-wise]
mjv
Don't you need a strtotime in there somewhere?
Doug Neiner
@dcneiner You bet! I forgot strtotime() the first time around, focussed that I was that it wasn't required for comparing the strings (as they are in the convenient YYYY-MM-DD format) but, indeed we need it to mess with them numerically...
mjv
+1  A: 

Here is how you can do it in PHP:

<?php
$array = array('2009-03-11', '2009-03-12', '2009-04-12', '2009-05-03', '2009-10-30');

# PHP was throwing errors until I set this
# it may be unnecessary depending on where you
# are using your code:
date_default_timezone_set("GMT");

$max = 0;
if( count($array) > 1 ){
    for($i = 0; $i < count($array) - 1; $i++){
        $start = strtotime( $array[$i] );
        $end   = strtotime( $array[$i + 1] );

        $diff  = $end - $start;
        if($diff > $max) $max = $diff; 
    }
}

$max = $max / (60*60*24);

?>

It loops throw your items (it executes one less time than there are number of items) and compares each one. If the comparison is larger than the next, it updates max. Time is in seconds, so after the loop is over we convert the seconds into days.

Doug Neiner
I'm afraid this approach wouldn't work for a couple of reasons. 1) it only tests a subset of all possible differences for example a difference between the first data in the array and say the third date is never evaluated. and 2) it assumes that the two dates considered as in ascending order. A simple test can fix #2, #1 requires introducing a inner loop. (This approach btw is less efficient that merely finding the min and max dates and substracting them)
mjv
The question was "largest interval between sequential dates" so the distance between date one and three would be irrelevant. Since the question listed the dates in ASC order I assumed that they were in the correct order from MySQL.
Doug Neiner
@dcneiner I understand your perspective, now. With that understanding of the question, your answer is of course correct. Rather than keying on the 'sequential', I went with the hint in the question that a 'sort' may be needed, hence my solution. +1 to you for a correct answer (in this view of the question), I'll edit mine to hilight the two possible responses. We're probably trying too hard anyway for a poorly worded question and a generally non acccepting (or even responding) OP ;-)
mjv
@mjv Thanks! I agree that we are most likely working too hard on this one! ;) Hopefully future searchers will glean from our effort!!
Doug Neiner
@mjv, I think I might call my question lazy if anything, rather than poorly worded. I think the question title is rather precise actually. I really appreciate everyone's effort. This is the most work anyone's taken the time to hammer out and such a short period of time. Thanks a bunch! I've yet to test any of these solutions. And of course I will respond. Without looking closely however, dcneiner and Chuck are probably closer to answering the title of this post.
rrrfusco
@rrrfusco Glad to hear it. I guess when we work late on Saturday night to solve a problem we get cranky and impatient to hear if our solution was chosen ;) Thanks for being good natured and not taking offense. :)
Doug Neiner
+1  A: 
ChuckO