I have two date formats:
Start Date: 2007-03-24 End Date: 2009-06-26
Now, I need to find the difference between the two in the following form:
2 years, 3 months and 2 days
How to do this in PHP?
#1st floor
Some time ago, I wrote the format ﹣ date function because it gives you many options on how to choose a date:
function format_date($date, $type, $seperator="-") { if($date) { $day = date("j", strtotime($date)); $month = date("n", strtotime($date)); $year = date("Y", strtotime($date)); $hour = date("H", strtotime($date)); $min = date("i", strtotime($date)); $sec = date("s", strtotime($date)); switch($type) { case 0: $date = date("Y".$seperator."m".$seperator."d",mktime($hour, $min, $sec, $month, $day, $year)); break; case 1: $date = date("D, F j, Y",mktime($hour, $min, $sec, $month, $day, $year)); break; case 2: $date = date("d".$seperator."m".$seperator."Y",mktime($hour, $min, $sec, $month, $day, $year)); break; case 3: $date = date("d".$seperator."M".$seperator."Y",mktime($hour, $min, $sec, $month, $day, $year)); break; case 4: $date = date("d".$seperator."M".$seperator."Y h:i A",mktime($hour, $min, $sec, $month, $day, $year)); break; case 5: $date = date("m".$seperator."d".$seperator."Y",mktime($hour, $min, $sec, $month, $day, $year)); break; case 6: $date = date("M",mktime($hour, $min, $sec, $month, $day, $year)); break; case 7: $date = date("Y",mktime($hour, $min, $sec, $month, $day, $year)); break; case 8: $date = date("j",mktime($hour, $min, $sec, $month, $day, $year)); break; case 9: $date = date("n",mktime($hour, $min, $sec, $month, $day, $year)); break; case 10: $diff = abs(strtotime($date) - strtotime(date("Y-m-d h:i:s"))); $years = floor($diff / (365*60*60*24)); $months = floor(($diff - $years * 365*60*60*24) / (30*60*60*24)); $days = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24)/ (60*60*24)); $date = $years . " years, " . $months . " months, " . $days . "days"; } } return($date); }
#2nd floor
DateInterval is great, but there are some caveats:
- Only for PHP 5.3 + (but this is no longer a good excuse)
- Only year, month, day, hour, minute and second (no week) are supported
- It calculates the difference of all the above + days (you can't get the difference in months only)
To overcome this problem, I wrote the following code (from @enobrev answer Improvement):
function date_dif($since, $until, $keys = 'year|month|week|day|hour|minute|second') { $date = array_map('strtotime', array($since, $until)); if ((count($date = array_filter($date, 'is_int')) == 2) && (sort($date) === true)) { $result = array_fill_keys(explode('|', $keys), 0); foreach (preg_grep('~^(?:year|month)~i', $result) as $key => $value) { while ($date[1] >= strtotime(sprintf('+%u %s', $value + 1, $key), $date[0])) { ++$value; } $date[0] = strtotime(sprintf('+%u %s', $result[$key] = $value, $key), $date[0]); } foreach (preg_grep('~^(?:year|month)~i', $result, PREG_GREP_INVERT) as $key => $value) { if (($value = intval(abs($date[0] - $date[1]) / strtotime(sprintf('%u %s', 1, $key), 0))) > 0) { $date[0] = strtotime(sprintf('+%u %s', $result[$key] = $value, $key), $date[0]); } } return $result; } return false; }
It runs two cycles. The first deals with relative intervals (years and months) by brute force, and the second computes additional absolute intervals (and therefore faster) by simple arithmetic:
echo humanize(date_dif('2007-03-24', '2009-07-31', 'second')); // 74300400 seconds echo humanize(date_dif('2007-03-24', '2009-07-31', 'minute|second')); // 1238400 minutes, 0 seconds echo humanize(date_dif('2007-03-24', '2009-07-31', 'hour|minute|second')); // 20640 hours, 0 minutes, 0 seconds echo humanize(date_dif('2007-03-24', '2009-07-31', 'year|day')); // 2 years, 129 days echo humanize(date_dif('2007-03-24', '2009-07-31', 'year|week')); // 2 years, 18 weeks echo humanize(date_dif('2007-03-24', '2009-07-31', 'year|week|day')); // 2 years, 18 weeks, 3 days echo humanize(date_dif('2007-03-24', '2009-07-31')); // 2 years, 4 months, 1 week, 0 days, 0 hours, 0 minutes, 0 seconds function humanize($array) { $result = array(); foreach ($array as $key => $value) { $result[$key] = $value . ' ' . $key; if ($value != 1) { $result[$key] .= 's'; } } return implode(', ', $result); }
#3rd floor
The best practice is to use PHP DateTime (and DateInterval )Object. Each date is encapsulated in a DateTime object, and you can then make a difference between the two:
$first_date = new DateTime("2012-11-30 17:03:30"); $second_date = new DateTime("2012-12-21 00:00:00");
The DateTime object will accept any strtotime() format. If you need a more specific date format, you can use the DateTime::createFromFormat() Create a DateTime object.
After instantiating two objects, you can use the DateTime::diff() Subtract the other.
$difference = $first_date->diff($second_date);
$difference now saves a DateInterval Object. var_dump() looks like this:
object(DateInterval) public 'y' => int 0 public 'm' => int 0 public 'd' => int 20 public 'h' => int 6 public 'i' => int 56 public 's' => int 30 public 'invert' => int 0 public 'days' => int 20
To format the DateInterval object, we need to check each value and exclude it if it is 0:
/** * Format an interval to show all existing components. * If the interval doesn't have a time component (years, months, etc) * That component won't be displayed. * * @param DateInterval $interval The interval * * @return string Formatted interval string. */ function format_interval(DateInterval $interval) { $result = ""; if ($interval->y) { $result .= $interval->format("%y years "); } if ($interval->m) { $result .= $interval->format("%m months "); } if ($interval->d) { $result .= $interval->format("%d days "); } if ($interval->h) { $result .= $interval->format("%h hours "); } if ($interval->i) { $result .= $interval->format("%i minutes "); } if ($interval->s) { $result .= $interval->format("%s seconds "); } return $result; }
Now all we have left is to call our function on the $difference DateInterval object:
echo format_interval($difference);
And we get the right results:
20 days, 6 hours, 56 minutes, 30 seconds
Complete code to achieve the goal:
/** * Format an interval to show all existing components. * If the interval doesn't have a time component (years, months, etc) * That component won't be displayed. * * @param DateInterval $interval The interval * * @return string Formatted interval string. */ function format_interval(DateInterval $interval) { $result = ""; if ($interval->y) { $result .= $interval->format("%y years "); } if ($interval->m) { $result .= $interval->format("%m months "); } if ($interval->d) { $result .= $interval->format("%d days "); } if ($interval->h) { $result .= $interval->format("%h hours "); } if ($interval->i) { $result .= $interval->format("%i minutes "); } if ($interval->s) { $result .= $interval->format("%s seconds "); } return $result; } $first_date = new DateTime("2012-11-30 17:03:30"); $second_date = new DateTime("2012-12-21 00:00:00"); $difference = $first_date->diff($second_date); echo format_interval($difference);
#4th floor
A simple function
function time_difference($time_1, $time_2, $limit = null) { $val_1 = new DateTime($time_1); $val_2 = new DateTime($time_2); $interval = $val_1->diff($val_2); $output = array( "year" => $interval->y, "month" => $interval->m, "day" => $interval->d, "hour" => $interval->h, "minute" => $interval->i, "second" => $interval->s ); $return = ""; foreach ($output AS $key => $value) { if ($value == 1) $return .= $value . " " . $key . " "; elseif ($value >= 1) $return .= $value . " " . $key . "s "; if ($key == $limit) return trim($return); } return trim($return); }
How to use
echo time_difference ($time_1, $time_2, "day");
2 years 8 months 2 days
#5th floor
You can also use the following code to return the date difference by the rounding score of $date1 = $duedate. //Specify expiration date echo $date2 = date ("Ymd"); / / current date $ts1 = strtotime ($date1); $ts2 = strtotime ($date2); $seconds_diff = $TS1 - $TS2; echo $DateDiff = ceil (($seconds_diff / 3600) / 24); / / returned in a few days
If you use php's floor method instead of ceil, it will reduce your rounding score. If your staging server time zone is different from the real-time site time zone, please check the difference here sometimes. In this case, you may get different results, so please change the conditions accordingly.