Today must be my lucky day. Received a second technical question. This one involves math, dates and PHP. Here’s the problem:

*Without using any built-in date functions, write a function dateDiff($date1, $date2) that finds the total number of days between two given dates. The dates will be in the format YYYY-MM-DD.*

*$d1 = “2005-01-01”;
$d2 = “2006-01-01”;
echo dateDiff($d1, $d2);
*

// Output: 365

The key point here is recognizing that time is a dripping bucket of water with each drop representing a second. In other words, you don’t even need to use PHP’s specific date functions to solve this. You just need to recognize that you need to take the strings representing date values for $d1 and $d2 and convert them into something more workable, seconds. The dandy little function mktime() can be useful here if we zero out its first three params for minutes, hours and seconds. By parsing each date string we can extract the month, day and year information to supply to mktime. After that we’ll subtract the first bucket of seconds from the second. Then we’ll convert the seconds into days. See code below:

<?php

define("DAY",60*60*24);

function myGetDate($theDate) {

list($yr,$mo,$day) = explode("-", $theDate);

$secs = mktime(0,0,0,$mo,$day,$yr);

return $secs;

}

function dateDiff($d1, $d2)

{

$diff = myGetDate($d2) - myGetDate($d1);

$totalNumDays = floor( $diff/DAY );

return ("The total number of days between $d1 and $d2 are: " . $totalNumDays . " days");

}

$d1 = "2005-01-01";

$d2 = "2006-01-01";

echo dateDiff($d1,$d2);

But, you may protest, what about using strtotime() instead of “rolling my own”? Well, we can certainly go with that, as follows:

<?php

define("DAY",60*60*24);

function dateDiff($d1, $d2)

{

$diff = strtotime($d2) - strtotime($d1);

$totalNumDays = floor( $diff/DAY );

return ("The number of days between $d1 and $d2 are: " . $totalNumDays . " days");

}

$d1 = "2005-01-01";

$d2 = "2006-01-01";

echo dateDiff($d1,$d2);

Mike(16:57:45) :Hi Sharon,

Thanks for working out a response to this and the previous question. I hope to not have offended you by the simplicity of these programming problems – they are simply part of a fairly trivial screening process designed to screen out people with a weak grasp of basic programming concepts, a lack of comprehension of basic algebra, or an inability to follow directions. Based on my experience, each of these traits is vital to a programmer who intends to take on anything more than the most mundane real-world tasks and work efficiently in a professional team environment.

I noticed that the first implementation of your dateDiff() function uses PHP’s built-in mktime() function. The goal of this question was for the interviewee to use her creativity to construct an algorithm that could accurately calculate the difference between two dates without using any built-in date functions such as strtotime() or mktime(). How might you construct this function given the prerequisite of not using these built-in functions?

In response to your previous post, I’d also like to point out that recursion, while potentially dangerous, is still an important tool in any good programmer’s toolkit. Some of the most efficient algorithms to common problems rely on recursion – take for example MergeSort. Even if you don’t use recursion on a regular basis, I find it an important concept for programmers to understand and this is why it’s included as part of our standard screening process.

Thanks again for your time and your responses, and I wish you the best of luck.

Mike

Sharon Lee Levy(17:34:26) :Mike,

Thanks for your detailed commentary. As I am sure you are aware, PHP was not designed for programmers to be algorithm conscious as much as to know what is the most appropriate PHP function to use. I once had a friend who crafted a beautiful algorithm using PHP only to have his endeavor criticized for being too slow by ignoring a certain PHP built-in function. The PHP built-ins are written in C and so when given a choice between rolling one’s own or using a built-in, the rule of thumb is to use a built-in.

That said let me return to solving the date problem another way without using PHP built-ins. Given that the values of the 2 dates in question have the same month and the same day, the only difference is the year values. So you could write something like this if you expected only the years to ever be different in order to calculate the number of days, as follows:

<?php

define("YEAR",365);

function dateDiff($d1, $d2)

{

return $d2 - $d1;

}

$d1 = "2005-01-01";

$d2 = "2006-01-01";

echo '<P>' . (dateDiff($d1,$d2) * YEAR ) . ' days.';

Mike(17:51:30) :Sharon,

Your point is well taken regarding PHP developers generally not bothering themselves with efficient algorithms. Under normal circumstances, there are very few times when it might be advantageous to write a custom implementation of something that is available as a built-in PHP function for the reasons you mention. These questions were intended as a programming exercise and not as a test of your knowledge of PHP.

The dateDiff() function would also be much more useful if it worked with arbitrary dates – eg. dateDiff(“2007-06-19”, “2008-04-05”). Under normal circumstances, of course, your strtotime() approach would be best (or mysql_query(“SELECT DATEDIFF(‘2007-06-19’, ‘2008-04-05’)”)) 😉

Mike

Sharon Lee Levy(19:28:00) :Mike,

There is another way to deal with the above problem that does not per se use the date/time built-ins but instead uses a handy built-in from the Calendar module, so here’s another way:

// w/o using date/time built-ins

function dateDiff($format, $d1, $d2)

{

$date_parts1=explode($format, $d1);

$date_parts2=explode($format, $d2);

$start_date = gregoriantojd($date_parts1[1], $date_parts1[2], $date_parts1[0]);

$end_date = gregoriantojd($date_parts2[1], $date_parts2[2], $date_parts2[0]);

return $end_date - $start_date;

}

$d1 = "2005-01-01";

$d2 = "2006-01-01";

echo "The number of days between dates $d1 and $d2 are: " . dateDiff('-',$d1, $d2) . ".";

This solution basically converts a string date value into a Julian date. Then one Julian date is subtracted from the other. This may be more efficient since there is no need to convert seconds to days as we are already working in units of days.

Sharon Lee Levy(23:11:39) :Mike,

Algorithms. I get it now. No built-ins. While you maintain that this shows programming skill, I would argue that it shows an understanding of the nature of the problem and that programming skill is what you add to how you handle the problem. But, let’s get to the good part, the code using an algorithm and not PHP’s built-ins. Here you go:

<?php

// function listing:

function is_leap_year($year)

{

$result = FALSE;

if ( ($year % 4 == 0) && ($year % 100 != 0)) {

$result = TRUE;

}

else

if ($year % 400 == 0) {

$result = TRUE;

}

else

{

$result = FALSE;

}

return ( $result );

}

function number_of_days_in_the_year($year)

{

return(is_leap_year($year))? 366 : 365;

}

function number_of_days_till_now($day,$mon,$year)

{

// non-leap year days of month totals

$months_of_year = array(31,28,31,30,31,30,31,31,30,31,30,31);

$sum=0;

for ($i=0; $i < $mon; $i++)

{

$sum += $months_of_year[ $i ];

}

$sum += $day;

if(is_leap_year( $year) && ($mon > 2)) {

$sum++; // incement to include leap year's Feb 29

}

return $sum;

}

function dateDiff($date1,$date2)

{

list($y, $m, $d) = explode("-", $date1);

list($y2, $m2, $d2) = explode("-", $date2);

if ( !($m2 - $m) && !($d2 - $d))

{

$num1 = number_of_days_in_the_year($y);

$num2 = 0;

for ($i=$y, $max=$y2; $i <= $max; $i++) {

$num2 += number_of_days_in_the_year($i);

};

return "The number of days between $date1 and $date2 are " . ($num2 - $num1);

}

else

{

$num = 0;

for ($i = 1, $max = $y2 - $y; $i < $max; $i++) {

$num += number_of_days_in_the_year($y + $i); // get days in years between dates

}

}

$numdaysOver2 = number_of_days_till_now($d2,$m2,$y2); // days over in 2nd date

$numberdaysinYear = number_of_days_in_the_year($y); // days in 1st date's year

$numdaysOver = number_of_days_till_now($d,$m,$y); // days over in 1st date's year

return "The number of days between $date1 and $date2 are " . ($num + $numdaysOver2 + ($numberdaysinYear - $numdaysOver));

}

// START with these two arbitary dates:

$date1 = '2005-01-01';

$date2 = '2006-01-01';

echo dateDiff($date1,$date2);

The approach here it to work with times in units of days according to our current calendar system which means by necessity having to deal with leap years. We also need to do a few calculations. If the months and days are the same in the two dates, as they are in this case, it’s a relatively simple subtraction problem. Relatively, b/c you still have to deal with the leap year issue. So, first get the number of days in the first date’s year which will either be 365 or 366 depending on whether there is a leap year. Then figure out the number of days in each year from the first date through the second date, checking for the leap year. Lastly total up these days for the second date and then subtract the first date’s days total from this second total.

Some of the code’s functions excluding dateDiff() are my translation of C code to PHP from: http://en.allexperts.com/q/C-1587/Computing-difference-dates-2.htm. The leap year algorithm I devised is based on the simple algorithm at http://en.wikipedia.org/wiki/Leap_year. dateDiff() is my attempt to use the translated C-to-PHP functions in a way that makes sense for subtracting one date string from another where both are in a hyphenated format YYYY-MM-DD in order to calculate the difference in days between the two dates.