Home » Php » datetime – How to calculate the difference between two dates using PHP?

# datetime – How to calculate the difference between two dates using PHP?

Questions:

I have two dates of the form:

``````Start Date: 2007-03-24
End Date: 2009-06-26
``````

Now I need to find the difference between these two in the following form:

``````2 years, 3 months and 2 days
``````

How can I do this in PHP?

Use this for legacy code (PHP < 5.3). For up to date solution see jurka’s answer below

You can use strtotime() to convert two dates to unix time and then calculate the number of seconds between them. From this it’s rather easy to calculate different time periods.

``````\$date1 = "2007-03-24";
\$date2 = "2009-06-26";

\$diff = abs(strtotime(\$date2) - strtotime(\$date1));

\$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));

printf("%d years, %d months, %d days\n", \$years, \$months, \$days);
``````

Edit: Obviously the preferred way of doing this is like described by jurka below. My code is generally only recommended if you don’t have PHP 5.3 or better.

Several people in the comments have pointed out that the code above is only an approximation. I still believe that for most purposes that’s fine, since the usage of a range is more to provide a sense of how much time has passed or remains rather than to provide precision – if you want to do that, just output the date.

Despite all that, I’ve decided to address the complaints. If you truly need an exact range but haven’t got access to PHP 5.3, use the code below (it should work in PHP 4 as well). This is a direct port of the code that PHP uses internally to calculate ranges, with the exception that it doesn’t take daylight savings time into account. That means that it’s off by an hour at most, but except for that it should be correct.

``````<?php

/**
* Calculate differences between two dates with precise semantics. Based on PHPs DateTime::diff()
* implementation by Derick Rethans. Ported to PHP by Emil H, 2011-05-02. No rights reserved.
*
* See here for original code:
* http://svn.php.net/viewvc/php/php-src/trunk/ext/date/lib/tm2unixtime.c?revision=302890&view=markup
* http://svn.php.net/viewvc/php/php-src/trunk/ext/date/lib/interval.c?revision=298973&view=markup
*/

function _date_range_limit(\$start, \$end, \$adj, \$a, \$b, \$result)
{
if (\$result[\$a] < \$start) {
\$result[\$b] -= intval((\$start - \$result[\$a] - 1) / \$adj) + 1;
\$result[\$a] += \$adj * intval((\$start - \$result[\$a] - 1) / \$adj + 1);
}

if (\$result[\$a] >= \$end) {
}

return \$result;
}

function _date_range_limit_days(\$base, \$result)
{
\$days_in_month_leap = array(31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
\$days_in_month = array(31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

_date_range_limit(1, 13, 12, "m", "y", &\$base);

\$year = \$base["y"];
\$month = \$base["m"];

if (!\$result["invert"]) {
while (\$result["d"] < 0) {
\$month--;
if (\$month < 1) {
\$month += 12;
\$year--;
}

\$leapyear = \$year % 400 == 0 || (\$year % 100 != 0 && \$year % 4 == 0);
\$days = \$leapyear ? \$days_in_month_leap[\$month] : \$days_in_month[\$month];

\$result["d"] += \$days;
\$result["m"]--;
}
} else {
while (\$result["d"] < 0) {
\$leapyear = \$year % 400 == 0 || (\$year % 100 != 0 && \$year % 4 == 0);
\$days = \$leapyear ? \$days_in_month_leap[\$month] : \$days_in_month[\$month];

\$result["d"] += \$days;
\$result["m"]--;

\$month++;
if (\$month > 12) {
\$month -= 12;
\$year++;
}
}
}

return \$result;
}

function _date_normalize(\$base, \$result)
{
\$result = _date_range_limit(0, 60, 60, "s", "i", \$result);
\$result = _date_range_limit(0, 60, 60, "i", "h", \$result);
\$result = _date_range_limit(0, 24, 24, "h", "d", \$result);
\$result = _date_range_limit(0, 12, 12, "m", "y", \$result);

\$result = _date_range_limit_days(&\$base, &\$result);

\$result = _date_range_limit(0, 12, 12, "m", "y", \$result);

return \$result;
}

/**
* Accepts two unix timestamps.
*/
function _date_diff(\$one, \$two)
{
\$invert = false;
if (\$one > \$two) {
list(\$one, \$two) = array(\$two, \$one);
\$invert = true;
}

\$key = array("y", "m", "d", "h", "i", "s");
\$a = array_combine(\$key, array_map("intval", explode(" ", date("Y m d H i s", \$one))));
\$b = array_combine(\$key, array_map("intval", explode(" ", date("Y m d H i s", \$two))));

\$result = array();
\$result["y"] = \$b["y"] - \$a["y"];
\$result["m"] = \$b["m"] - \$a["m"];
\$result["d"] = \$b["d"] - \$a["d"];
\$result["h"] = \$b["h"] - \$a["h"];
\$result["i"] = \$b["i"] - \$a["i"];
\$result["s"] = \$b["s"] - \$a["s"];
\$result["invert"] = \$invert ? 1 : 0;
\$result["days"] = intval(abs((\$one - \$two)/86400));

if (\$invert) {
_date_normalize(&\$a, &\$result);
} else {
_date_normalize(&\$b, &\$result);
}

return \$result;
}

\$date = "1986-11-10 19:37:22";

print_r(_date_diff(strtotime(\$date), time()));
print_r(_date_diff(time(), strtotime(\$date)));
``````

I suggest to use DateTime and DateInterval objects.

``````\$date1 = new DateTime("2007-03-24");
\$date2 = new DateTime("2009-06-26");
\$interval = \$date1->diff(\$date2);
echo "difference " . \$interval->y . " years, " . \$interval->m." months, ".\$interval->d." days ";

// shows the total amount of days (not divided into years, months and days like above)
echo "difference " . \$interval->days . " days ";
``````

From the manual:

As of PHP 5.2.2, DateTime objects can be compared using comparison operators.

``````\$date1 = new DateTime("now");
\$date2 = new DateTime("tomorrow");

var_dump(\$date1 == \$date2); // bool(false)
var_dump(\$date1 < \$date2);  // bool(true)
var_dump(\$date1 > \$date2);  // bool(false)
``````

The best course of action is using PHP’s `DateTime` (and `DateInterval`) objects. Each date is encapsulated in a `DateTime` object, and then a difference between the two can be made:

``````\$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 format `strtotime()` would. If an even more specific date format is needed, `DateTime::createFromFormat()` can be used to create the `DateTime` object.

After both objects were instantiated, you substract one from the other with `DateTime::diff()`.

``````\$difference = \$first_date->diff(\$second_date);
``````

`\$difference` now holds a `DateInterval` object with the difference information. A `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’ll need check each value and exclude it if it’s 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;
}
``````

All that’s left now is to call our function on the `\$difference` `DateInterval` object:

``````echo format_interval(\$difference);
``````

And we get the correct result:

20 days 6 hours 56 minutes 30 seconds

The complete code used 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);
``````

View Hours and Minuts and Seconds..

``````\$date1 = "2008-11-01 22:45:00";

\$date2 = "2009-12-04 13:44:01";

\$diff = abs(strtotime(\$date2) - strtotime(\$date1));

\$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));

\$hours   = floor((\$diff - \$years * 365*60*60*24 - \$months*30*60*60*24 - \$days*60*60*24)/ (60*60));

\$minuts  = floor((\$diff - \$years * 365*60*60*24 - \$months*30*60*60*24 - \$days*60*60*24 - \$hours*60*60)/ 60);

\$seconds = floor((\$diff - \$years * 365*60*60*24 - \$months*30*60*60*24 - \$days*60*60*24 - \$hours*60*60 - \$minuts*60));

printf("%d years, %d months, %d days, %d hours, %d minuts\n, %d seconds\n", \$years, \$months, \$days, \$hours, \$minuts, \$seconds);
``````

Take a look at the following link. This is the best answer I’ve found so far.. 🙂

``````function dateDiff (\$d1, \$d2) {

// Return the number of days between the two dates:
return round(abs(strtotime(\$d1) - strtotime(\$d2))/86400);

} // end function dateDiff
``````

It doesn’t matter which date is earlier or later when you pass in the
date parameters. The function uses the PHP ABS() absolute value to
always return a postive number as the number of days between the two
dates.

Keep in mind that the number of days between the two dates is NOT
inclusive of both dates. So if you are looking for the number of days
represented by all the dates between and including the dates entered,
you will need to add one (1) to the result of this function.

For example, the difference (as returned by the above function)
between 2013-02-09 and 2013-02-14 is 5. But the number of days or
dates represented by the date range 2013-02-09 – 2013-02-14 is 6.

http://www.bizinfosys.com/php/date-difference.html

I voted for jurka‘s answer as that’s my favorite, but I have a pre-php.5.3 version…

I found myself working on a similar problem – which is how I got to this question in the first place – but just needed a difference in hours. But my function solved this one pretty nicely as well and I don’t have anywhere in my own library to keep it where it won’t get lost and forgotten, so… hope this is useful to someone.

``````/**
*
* @param DateTime \$oDate1
* @param DateTime \$oDate2
* @return array
*/
function date_diff_array(DateTime \$oDate1, DateTime \$oDate2) {
\$aIntervals = array(
'year'   => 0,
'month'  => 0,
'week'   => 0,
'day'    => 0,
'hour'   => 0,
'minute' => 0,
'second' => 0,
);

foreach(\$aIntervals as \$sInterval => &\$iInterval) {
while(\$oDate1 <= \$oDate2){
\$oDate1->modify('+1 ' . \$sInterval);
if (\$oDate1 > \$oDate2) {
\$oDate1->modify('-1 ' . \$sInterval);
break;
} else {
\$iInterval++;
}
}
}

return \$aIntervals;
}
``````

And the test:

``````\$oDate = new DateTime();
\$oDate->modify('+111402189 seconds');
var_dump(\$oDate);
var_dump(date_diff_array(new DateTime(), \$oDate));
``````

And the result:

``````object(DateTime)
public 'date' => string '2014-04-29 18:52:51' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'America/New_York' (length=16)

array
'year'   => int 3
'month'  => int 6
'week'   => int 1
'day'    => int 4
'hour'   => int 9
'minute' => int 3
'second' => int 8
``````

I got the original idea from here, which I modified for my uses (and I hope my modification will show on that page as well).

You can very easily remove intervals you don’t want (say “week”) by removing them from the `\$aIntervals` array, or maybe adding an `\$aExclude` parameter, or just filter them out when you output the string.

``````<?php
\$today = strtotime("2011-02-03 00:00:00");
\$myBirthDate = strtotime("1964-10-30 00:00:00");
printf("Days since my birthday: ", (\$today - \$myBirthDate)/60/60/24);
?>
``````

I don’t know if you are using a PHP framework or not, but a lot of PHP frameworks have date/time libraries and helpers to help keep you from reinventing the wheel.

For example CodeIgniter has the `timespan()` function. Simply input two Unix timestamps and it will automatically generate a result like this:

``````1 Year, 10 Months, 2 Weeks, 5 Days, 10 Hours, 16 Minutes
``````

http://codeigniter.com/user_guide/helpers/date_helper.html

### Use example :

``````echo time_diff_string('2013-05-01 00:22:35', 'now');
echo time_diff_string('2013-05-01 00:22:35', 'now', true);
``````

### Output :

``````4 months ago
4 months, 2 weeks, 3 days, 1 hour, 49 minutes, 15 seconds ago
``````

### Function :

``````function time_diff_string(\$from, \$to, \$full = false) {
\$from = new DateTime(\$from);
\$to = new DateTime(\$to);
\$diff = \$to->diff(\$from);

\$diff->w = floor(\$diff->d / 7);
\$diff->d -= \$diff->w * 7;

\$string = array(
'y' => 'year',
'm' => 'month',
'w' => 'week',
'd' => 'day',
'h' => 'hour',
'i' => 'minute',
's' => 'second',
);
foreach (\$string as \$k => &\$v) {
if (\$diff->\$k) {
\$v = \$diff->\$k . ' ' . \$v . (\$diff->\$k > 1 ? 's' : '');
} else {
unset(\$string[\$k]);
}
}

if (!\$full) \$string = array_slice(\$string, 0, 1);
return \$string ? implode(', ', \$string) . ' ago' : 'just now';
}
``````

I have some simple logic for that:

``````<?php
per_days_diff('2011-12-12','2011-12-29')
function per_days_diff(\$start_date, \$end_date) {
\$per_days = 0;
\$noOfWeek = 0;
\$noOfWeekEnd = 0;
\$highSeason=array("7", "8");

\$current_date = strtotime(\$start_date);
\$current_date += (24 * 3600);
\$end_date = strtotime(\$end_date);

\$seassion = (in_array(date('m', \$current_date), \$highSeason))?"2":"1";

\$noOfdays = array('');

while (\$current_date <= \$end_date) {
if (\$current_date <= \$end_date) {
\$date = date('N', \$current_date);
array_push(\$noOfdays,\$date);
\$current_date = strtotime('+1 day', \$current_date);
}
}

\$finalDays = array_shift(\$noOfdays);
//print_r(\$noOfdays);
\$weekFirst = array("week"=>array(),"weekEnd"=>array());
for(\$i = 0; \$i < count(\$noOfdays); \$i++)
{
if (\$noOfdays[\$i] == 1)
{
//echo "This is week";
//echo "<br/>";
if(\$noOfdays[\$i+6]==7)
{
\$noOfWeek++;
\$i=\$i+6;
}
else
{
\$per_days++;
}
//array_push(\$weekFirst["week"],\$day);
}
else if(\$noOfdays[\$i]==5)
{
//echo "This is weekend";
//echo "<br/>";
if(\$noOfdays[\$i+2] ==7)
{
\$noOfWeekEnd++;
\$i = \$i+2;
}
else
{
\$per_days++;
}
//echo "After weekend value:- ".\$i;
//echo "<br/>";
}
else
{
\$per_days++;
}
}

/*echo \$noOfWeek;
echo "<br/>";
echo \$noOfWeekEnd;
echo "<br/>";
print_r(\$per_days);
echo "<br/>";
print_r(\$weekFirst);
*/

\$duration = array("weeks"=>\$noOfWeek, "weekends"=>\$noOfWeekEnd, "perDay"=>\$per_days, "seassion"=>\$seassion);
return \$duration;
?>
``````

You can use the

``````getdate()
``````

function which returns an array containing all elements of the date/time supplied:

``````\$diff = abs(\$endDate - \$startDate);
\$my_t=getdate(\$diff);
print("\$my_t[year] years, \$my_t[month] months and \$my_t[mday] days");
``````

If your start and end dates are in string format then use

``````\$startDate = strtotime(\$startDateStr);
\$endDate = strtotime(\$endDateStr);
``````

before the above code

``````// If you just want to see the year difference then use this function.
// Using the logic I've created you may also create month and day difference
// which I did not provide here so you may have the efforts to use your brain.
// :)
\$date1='2009-01-01';
\$date2='2010-01-01';
echo getYearDifference (\$date1,\$date2);
function getYearDifference(\$date1=strtotime(\$date1),\$date2=strtotime(\$date2)){
\$year = 0;
while(\$date2 > \$date1 = strtotime('+1 year', \$date1)){
++\$year;
}
return \$year;
}
``````

This is my function. Required PHP >= 5.3.4. It use DateTime class. Very fast, quick and can do the difference between two dates or even the so called “time since”.

``````if(function_exists('grk_Datetime_Since') === FALSE){
function grk_Datetime_Since(\$From, \$To='', \$Prefix='', \$Suffix=' ago', \$Words=array()){
#   Est-ce qu'on calcul jusqu'à un moment précis ? Probablement pas, on utilise maintenant
if(empty(\$To) === TRUE){
\$To = time();
}

#   On va s'assurer que \$From est numérique
if(is_int(\$From) === FALSE){
\$From = strtotime(\$From);
};

#   On va s'assurer que \$To est numérique
if(is_int(\$To) === FALSE){
\$To = strtotime(\$To);
}

#   On a une erreur ?
if(\$From === FALSE OR \$From === -1 OR \$To === FALSE OR \$To === -1){
return FALSE;
}

#   On va créer deux objets de date
\$From = new DateTime(@date('Y-m-d H:i:s', \$From), new DateTimeZone('GMT'));
\$To   = new DateTime(@date('Y-m-d H:i:s', \$To), new DateTimeZone('GMT'));

#   On va calculer la différence entre \$From et \$To
if((\$Diff = \$From->diff(\$To)) === FALSE){
return FALSE;
}

#   On va merger le tableau des noms (par défaut, anglais)
\$Words = array_merge(array(
'year'      => 'year',
'years'     => 'years',
'month'     => 'month',
'months'    => 'months',
'week'      => 'week',
'weeks'     => 'weeks',
'day'       => 'day',
'days'      => 'days',
'hour'      => 'hour',
'hours'     => 'hours',
'minute'    => 'minute',
'minutes'   => 'minutes',
'second'    => 'second',
'seconds'   => 'seconds'
), \$Words);

#   On va créer la chaîne maintenant
if(\$Diff->y > 1){
\$Text = \$Diff->y.' '.\$Words['years'];
} elseif(\$Diff->y == 1){
\$Text = '1 '.\$Words['year'];
} elseif(\$Diff->m > 1){
\$Text = \$Diff->m.' '.\$Words['months'];
} elseif(\$Diff->m == 1){
\$Text = '1 '.\$Words['month'];
} elseif(\$Diff->d > 7){
\$Text = ceil(\$Diff->d/7).' '.\$Words['weeks'];
} elseif(\$Diff->d == 7){
\$Text = '1 '.\$Words['week'];
} elseif(\$Diff->d > 1){
\$Text = \$Diff->d.' '.\$Words['days'];
} elseif(\$Diff->d == 1){
\$Text = '1 '.\$Words['day'];
} elseif(\$Diff->h > 1){
\$Text = \$Diff->h.' '.\$Words['hours'];
} elseif(\$Diff->h == 1){
\$Text = '1 '.\$Words['hour'];
} elseif(\$Diff->i > 1){
\$Text = \$Diff->i.' '.\$Words['minutes'];
} elseif(\$Diff->i == 1){
\$Text = '1 '.\$Words['minute'];
} elseif(\$Diff->s > 1){
\$Text = \$Diff->s.' '.\$Words['seconds'];
} else {
\$Text = '1 '.\$Words['second'];
}

return \$Prefix.\$Text.\$Suffix;
}
}
``````

I would prefer to use `date_create` and `date_diff` objects.

Code:

``````\$date1 = date_create("2007-03-24");
\$date2 = date_create("2009-06-26");

\$dateDifference = date_diff(\$date1, \$date2)->format('%y years, %m months and %d days');

echo \$dateDifference;
``````

Output:

``````2 years, 3 months and 2 days
``````

For more info read PHP `date_diff` manual

According to manual `date_diff` is an alias of
DateTime::diff()

This will try to detect whether a timestamp was given or not, and will also return future dates/times as negative values:

``````<?php

function time_diff(\$start, \$end = NULL, \$convert_to_timestamp = FALSE) {
// If \$convert_to_timestamp is not explicitly set to TRUE,
// check to see if it was accidental:
if (\$convert_to_timestamp || !is_numeric(\$start)) {
// If \$convert_to_timestamp is TRUE, convert to timestamp:
\$timestamp_start = strtotime(\$start);
}
else {
// Otherwise, leave it as a timestamp:
\$timestamp_start = \$start;
}
// Same as above, but make sure \$end has actually been overridden with a non-null,
// non-empty, non-numeric value:
if (!is_null(\$end) && (!empty(\$end) && !is_numeric(\$end))) {
\$timestamp_end = strtotime(\$end);
}
else {
// If \$end is NULL or empty and non-numeric value, assume the end time desired
// is the current time (useful for age, etc):
\$timestamp_end = time();
}
// Regardless, set the start and end times to an integer:
\$start_time = (int) \$timestamp_start;
\$end_time = (int) \$timestamp_end;

// Assign these values as the params for \$then and \$now:
\$start_time_var = 'start_time';
\$end_time_var = 'end_time';
// Use this to determine if the output is positive (time passed) or negative (future):
\$pos_neg = 1;

// If the end time is at a later time than the start time, do the opposite:
if (\$end_time <= \$start_time) {
\$start_time_var = 'end_time';
\$end_time_var = 'start_time';
\$pos_neg = -1;
}

// Convert everything to the proper format, and do some math:
\$then = new DateTime(date('Y-m-d H:i:s', \$\$start_time_var));
\$now = new DateTime(date('Y-m-d H:i:s', \$\$end_time_var));

\$years_then = \$then->format('Y');
\$years_now = \$now->format('Y');
\$years = \$years_now - \$years_then;

\$months_then = \$then->format('m');
\$months_now = \$now->format('m');
\$months = \$months_now - \$months_then;

\$days_then = \$then->format('d');
\$days_now = \$now->format('d');
\$days = \$days_now - \$days_then;

\$hours_then = \$then->format('H');
\$hours_now = \$now->format('H');
\$hours = \$hours_now - \$hours_then;

\$minutes_then = \$then->format('i');
\$minutes_now = \$now->format('i');
\$minutes = \$minutes_now - \$minutes_then;

\$seconds_then = \$then->format('s');
\$seconds_now = \$now->format('s');
\$seconds = \$seconds_now - \$seconds_then;

if (\$seconds < 0) {
\$minutes -= 1;
\$seconds += 60;
}
if (\$minutes < 0) {
\$hours -= 1;
\$minutes += 60;
}
if (\$hours < 0) {
\$days -= 1;
\$hours += 24;
}
\$months_last = \$months_now - 1;
if (\$months_now == 1) {
\$years_now -= 1;
\$months_last = 12;
}

// "Thirty days hath September, April, June, and November" ;)
if (\$months_last == 9 || \$months_last == 4 || \$months_last == 6 || \$months_last == 11) {
\$days_last_month = 30;
}
else if (\$months_last == 2) {
// Factor in leap years:
if ((\$years_now % 4) == 0) {
\$days_last_month = 29;
}
else {
\$days_last_month = 28;
}
}
else {
\$days_last_month = 31;
}
if (\$days < 0) {
\$months -= 1;
\$days += \$days_last_month;
}
if (\$months < 0) {
\$years -= 1;
\$months += 12;
}

// Finally, multiply each value by either 1 (in which case it will stay the same),
// or by -1 (in which case it will become negative, for future dates).
// Note: 0 * 1 == 0 * -1 == 0
\$out = new stdClass;
\$out->years = (int) \$years * \$pos_neg;
\$out->months = (int) \$months * \$pos_neg;
\$out->days = (int) \$days * \$pos_neg;
\$out->hours = (int) \$hours * \$pos_neg;
\$out->minutes = (int) \$minutes * \$pos_neg;
\$out->seconds = (int) \$seconds * \$pos_neg;
return \$out;
}
``````

Example usage:

``````<?php
\$birthday = 'June 2, 1971';
\$check_age_for_this_date = 'June 3, 1999 8:53pm';
\$age = time_diff(\$birthday, \$check_age_for_this_date)->years;
print \$age;// 28
``````

Or:

``````<?php
\$christmas_2020 = 'December 25, 2020';
\$countdown = time_diff(\$christmas_2020);
print_r(\$countdown);
``````

“if” the date is stored in MySQL, I find it easier to do the difference calculation at the database level… Then based on the Day, Hour, Min, Sec output, parse and display results as appropriate…

``````mysql> select firstName, convert_tz(loginDate, '+00:00', '-04:00') as loginDate, TIMESTAMPDIFF(DAY, loginDate, now()) as 'Day', TIMESTAMPDIFF(HOUR, loginDate, now())+4 as 'Hour', TIMESTAMPDIFF(MINUTE, loginDate, now())+(60*4) as 'Min', TIMESTAMPDIFF(SECOND, loginDate, now())+(60*60*4) as 'Sec' from User_ where userId != '10158' AND userId != '10198' group by emailAddress order by loginDate desc;
+-----------+---------------------+------+------+------+--------+
| firstName | loginDate           | Day  | Hour | Min  | Sec    |
+-----------+---------------------+------+------+------+--------+
| Peter     | 2014-03-30 18:54:40 |    0 |    4 |  244 |  14644 |
| Keith     | 2014-03-30 18:54:11 |    0 |    4 |  244 |  14673 |
| Andres    | 2014-03-28 09:20:10 |    2 |   61 | 3698 | 221914 |
| Nadeem    | 2014-03-26 09:33:43 |    4 |  109 | 6565 | 393901 |
+-----------+---------------------+------+------+------+--------+
4 rows in set (0.00 sec)
``````

I found your article on the following page, which contains a number of references for PHP date time calculations.

Calculate the difference between two Dates (and time) using PHP. The following page provides a range of different methods (7 in total) for performing date / time calculations using PHP, to determine the difference in time (hours, munites), days, months or years between two dates.

You can also use following code to return date diff by round fractions up
\$date1 = \$duedate; // assign due date
echo \$date2 = date(“Y-m-d”); // current date
\$ts1 = strtotime(\$date1);
\$ts2 = strtotime(\$date2);
\$seconds_diff = \$ts1 – \$ts2;
echo \$datediff = ceil((\$seconds_diff/3600)/24); // return in days

If you use floor method of php instead of ceil it will return you the round fraction down. Please check the difference here, some times if your staging servers timezone is different then the live site time zone in that case you may get different results so change the conditions accordingly.

``````\$date1 = date_create('2007-03-24');
\$date2 = date_create('2009-06-26');
\$interval = date_diff(\$date1, \$date2);
echo "difference : " . \$interval->y . " years, " . \$interval->m." months, ".\$interval->d." days ";
``````

you can always use the following function that can return the age in years and months (ie. 1 Year 4 Months)

``````function getAge(\$dob, \$age_at_date)
{
\$d1 = new DateTime(\$dob);
\$d2 = new DateTime(\$age_at_date);
\$age = \$d2->diff(\$d1);
\$years = \$age->y;
\$months = \$age->m;

return \$years.'.'.months;
}
``````

or if you want the age to be calculated at the current date, you can use

``````function getAge(\$dob)
{
\$d1 = new DateTime(\$dob);
\$d2 = new DateTime(date());
\$age = \$d2->diff(\$d1);
\$years = \$age->y;
\$months = \$age->m;

return \$years.'.'.months;
}
``````

For php version >=5.3 : Create two date objects and then use `date_diff()` function. It will return php DateInterval object. see documentation

``````\$date1=date_create("2007-03-24");
\$date2=date_create("2009-06-26");
\$diff=date_diff(\$date1,\$date2);
echo \$diff->format("%R%a days");
``````

I had the same problem with PHP 5.2 and solved it with MySQL. Might not be exactly what you’re looking for, but this will do the trick and return the number of days:

``````\$datediff_q = \$dbh->prepare("SELECT DATEDIFF(:date2, :date1)");
\$datediff_q->bindValue(':date1', '2007-03-24', PDO::PARAM_STR);
\$datediff_q->bindValue(':date2', '2009-06-26', PDO::PARAM_STR);
\$datediff = (\$datediff_q->execute()) ? \$datediff_q->fetchColumn(0) : false;
``````

Since everyone is posting code samples, here is another version.

I wanted a function to display differences from seconds to years (just one unit). For periods over 1 day, I wanted it to rollover at midnight (10am Monday seen from 9am Wednesday is 2 days ago, not 1). And for periods over a month, I wanted the rollover to be on the same day of the month (including for 30/31 day months & leap years).

This is what I came up with:

``````/**
* Returns how long ago something happened in the past, showing it
* as n seconds / minutes / hours / days / weeks / months / years ago.
*
* For periods over a day, it rolls over at midnight (so doesn't depend
* on current time of day), and it correctly accounts for month-lengths
* and leap-years (months and years rollover on current day of month).
*
* \$param string \$timestamp in DateTime format
* \$return string description of interval
*/
function ago(\$timestamp)
{
\$then = date_create(\$timestamp);

// for anything over 1 day, make it rollover on midnight
\$today = date_create('tomorrow'); // ie end of today
\$diff = date_diff(\$then, \$today);

if (\$diff->y > 0) return \$diff->y.' year'.(\$diff->y>1?'s':'').' ago';
if (\$diff->m > 0) return \$diff->m.' month'.(\$diff->m>1?'s':'').' ago';
\$diffW = floor(\$diff->d / 7);
if (\$diffW > 0) return \$diffW.' week'.(\$diffW>1?'s':'').' ago';
if (\$diff->d > 1) return \$diff->d.' day'.(\$diff->d>1?'s':'').' ago';

// for anything less than 1 day, base it off 'now'
\$now = date_create();
\$diff = date_diff(\$then, \$now);

if (\$diff->d > 0) return 'yesterday';
if (\$diff->h > 0) return \$diff->h.' hour'.(\$diff->h>1?'s':'').' ago';
if (\$diff->i > 0) return \$diff->i.' minute'.(\$diff->i>1?'s':'').' ago';
return \$diff->s.' second'.(\$diff->s==1?'':'s').' ago';
}
``````

Some time ago I wrote a `format_date` function as this gives many options on how you want your 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);
}
``````

Very simple:

``````    <?php
\$date1 = date_create("2007-03-24");
echo "Start date: ".\$date1->format("Y-m-d")."<br>";
\$date2 = date_create("2009-06-26");
echo "End date: ".\$date2->format("Y-m-d")."<br>";
\$diff = date_diff(\$date1,\$date2);
echo "Difference between start date and end date: ".\$diff->format("%y years, %m months and %d days")."<br>";
?>
``````

PHP: date_diff – Manual

Note that it’s for PHP 5.3.0 or greater.

An easy 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);
}
``````

Use like

`echo time_difference (\$time_1, \$time_2, "day");`

Will return like `2 years 8 months 2 days`

Here is the runnable code

``````\$date1 = date_create('2007-03-24');
\$date2 = date_create('2009-06-26');
\$diff1 = date_diff(\$date1,\$date2);
\$daysdiff = \$diff1->format("%R%a");
\$daysdiff = abs(\$daysdiff);
``````

Try this very simple answer using date_diff(), this is tested.

``````\$date1 = date_create("2017-11-27");
\$date2 = date_create("2018-12-29");
\$diff=date_diff(\$date1,\$date2);
\$months = \$diff->format("%m months");
\$years = \$diff->format("%y years");
\$days = \$diff->format("%d days");

echo \$years .' '.\$months.' '.\$days;
``````

the output is:

``````1 years 1 months 2 days
``````

I’m using the following function which I wrote, when PHP 5.3 (respectively date_diff()) is not available:

``````        function dateDifference(\$startDate, \$endDate)
{
\$startDate = strtotime(\$startDate);
\$endDate = strtotime(\$endDate);
if (\$startDate === false || \$startDate < 0 || \$endDate === false || \$endDate < 0 || \$startDate > \$endDate)
return false;

\$years = date('Y', \$endDate) - date('Y', \$startDate);

\$endMonth = date('m', \$endDate);
\$startMonth = date('m', \$startDate);

// Calculate months
\$months = \$endMonth - \$startMonth;
if (\$months <= 0)  {
\$months += 12;
\$years--;
}
if (\$years < 0)
return false;

// Calculate the days
\$measure = (\$months == 1) ? 'month' : 'months';
\$days = \$endDate - strtotime('+' . \$months . ' ' . \$measure, \$startDate);
\$days = date('z', \$days);

return array(\$years, \$months, \$days);
}
``````

`DateInterval` is great but it has a couple of caveats:

1. only for PHP 5.3+ (but that’s really not a good excuse anymore)
2. only supports years, months, days, hours, minutes and seconds (no weeks)
3. it calculates the difference with all of the above + days (you can’t get the difference in months only)

To overcome that, I coded the following (improved from @enobrev answer):

``````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 >= strtotime(sprintf('+%u %s', \$value + 1, \$key), \$date))
{
++\$value;
}

\$date = strtotime(sprintf('+%u %s', \$result[\$key] = \$value, \$key), \$date);
}

foreach (preg_grep('~^(?:year|month)~i', \$result, PREG_GREP_INVERT) as \$key => \$value)
{
if ((\$value = intval(abs(\$date - \$date) / strtotime(sprintf('%u %s', 1, \$key), 0))) > 0)
{
\$date = strtotime(sprintf('+%u %s', \$result[\$key] = \$value, \$key), \$date);
}
}

return \$result;
}

return false;
}
``````

It runs two loops; the first one deals with the relative intervals (years and months) via brute-forcing, and the second one computes the additional absolute intervals with simple arithmetic (so it’s faster):

``````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);
}
``````