Calculating PHP time difference (time ago)

It is a common problem in any CMS to show the last time a modification has been made. Our function will elaborate a bit on the PHP time difference and allow the user option of “accuracy” or show how many time intervals to be included in the “time ago” statement. For example if an article was posted 2 years 4 months 5 hours 33 minutes and 10 seconds ago we can limit the function to show only 1 level of accuracy – years or more.

PHP Time difference method 1

to calculate the time difference that I am going to discuss envolves using the DateTime object which unfortunately requires PHP > 5.2. Anyways I would advise to use at least PHP > 5.3 because of the major improvements made with version 5.3. you can read more about the DateTime class.
Here is the complete code for the function:

function dateDiff1(DateTime $old_date, $accuracy = 2, DateTime $now = NULL)
{

try {
if ($now == NULL) {
$now = new DateTime("now");
}

if (!is_int($accuracy) || $accuracy < 1 || $accuracy > 6) {
throw new InvalidArgumentException("Date Accuracy should be an integer between 1 and 6");
}

if ($old_date > $now) {
throw new InvalidArgumentException('The previous date cannot be greater than todays date');
}

$difference = $now->diff($old_date);

$intervals = array('y' => 'year', 'm' => 'month', 'd' => 'day', 'h' => 'hour', 'i' => 'minute', 's' => 'second');

$i = 0;
$result = '';
foreach ($intervals as $interval => $name) {
if ($difference->$interval > 1) {
$result .= $difference->$interval . $intervals[$interval] . 's';
$i++;
} elseif ($difference->$interval == 1) {
$result .= $difference->$interval . $intervals[$interval];
$i++;
}
if ($i == $accuracy) {
break;
}
}

return $result;

} catch (Exception $e) {
echo $e;
return NULL;
}

}

Now the explanation for it:

The first part of the function basically runs some checks to insure we are passing the correct variables and throws some exceptions that can be handled in the try…catch block. Obviously the checks and the whole try…catch block are not necessary but they are a good thing to have especially if you want to log errors. As you can see below we are comparing the two DateTime objects with regular arithmetics operators. The DateTime class allows that:

//some regular checks to validate our function parameters and avoid unexpected results
if ($now == NULL) {
$now = new DateTime("now");
}

if (!is_int($accuracy) || $accuracy < 1 || $accuracy > 6) {
throw new InvalidArgumentException("Date Accuracy should be an integer between 1 and 6");
}

//you can use arithmetics operators on DateTime objects
if ($old_date > $now) {
throw new InvalidArgumentException('The previous date cannot be greater than todays date');
}
[/syntax_prettify]

The second part of the function calculates the difference between the two DateTime objects' dates and return a <a href="http://www.php.net/manual/en/class.dateinterval.php" target="_blank">DateInterval</a> object. The intervals variable just holds the mapping between the DateInterval object member names and some text representation of them that you can change for different languages:


$difference = $now->diff($old_date);

$intervals = array('y' => 'year', 'm' => 'month', 'd' => 'day', 'h' => 'hour', 'i' => 'minute', 's' => 'second');

The limitation here is that only the year, month, day and hour, minute and second time intervals are returned by the DateInterval object, so if you want to include week, decade or some custom intervals you may want to skip that method and check the other method I will be discussing later in the article.

Lastly the part of our function that does the actual work is:

$i = 0; // set this variable to increment on each interval pass
$result = ''; //initialize empty string to hold the result
foreach ($intervals as $interval => $name) {
//if the difference for that time interval is greater than one add a trailing s to the interval word
if ($difference->$interval > 1) {
$result .= $difference->$interval . $intervals[$interval] . 's';
$i++;
//if interval value is exactly 1 dont add an s
} elseif ($difference->$interval == 1) {
$result .= $difference->$interval . $intervals[$interval];
$i++;
}
//if we reach the desired accuracy level exit out of the loop
if ($i == $accuracy) {
break;
}
}

return $result;

The comments pretty much explain how it works. We check if the current interval has a value of 1 or more than 1 and adjust the string literal accordingly and then if the desired level of accuracy is reached we dont go through the lower intervals and exit the loop.

PHP Time difference method 2

some of you may not want to use the DateTime object or may like to have different intervals in the “time ago” statement that the DateInterval object does not support. In that case you can try:

function dateDiff2($old_date, $accuracy = 2, $now = Null)
{
//you can specify more periods starting from smaller to larger!
$periods = array("second" => 1, "minute" => 60, "hour" => 3600, "day" => 86400, "week" => 604800, "month" => 2630880, "year" => 31570560, "decade" => 315705600);
//we inverse the array in order to check for the larger periods first
$periods = array_reverse($periods);

if ($now === Null) {
$now = time();
}

if (!is_int($accuracy) || $accuracy < 1 || $accuracy > count($periods)) {
throw new InvalidArgumentException("Date Accuracy should be an integer between 1 and 6");
}

$difference = $now - $old_date;
if ($difference < 0) { throw new InvalidArgumentException('The previous date cannot be greater than todays date'); } $result = ''; $i = 0; foreach($periods as $k=>$v){
//round the number of times a large period fits in the time difference
$tmp = floor($difference/$v);
if($tmp > 1){
$result .= $tmp . $k . 's';
$i++;
}elseif($tmp == 1){
$result .= $tmp . $k;
$i++;
}
//substract the reminder of the division if the difference can be divided by the current period in the loop
$difference = $difference%$v;
//break the loop if desired accuracy is reached
if($i == $accuracy){
break;
}
}

return $result;

}

This function does absolutely the same but in that case we use unix timestamps as returned by time() and strototime() functions instead of DateTime objects. You may have noticed the $now parameter in both functions. If you set the now parameter to a valid DateTime object in the first case or a valid unix timestamp in the second instead of using the current system time the ago statement will return the difference between the $now variable and the $old_time variable in the same format.

Let me know if those functions where helpful to you or if you have any other questions/suggestions regarding them!

  • rushit dawda

    Its good. bt can you make it bit more simpler

    • ferisoft

      Which part confuses you ?

  • Ciprian Radu

    Does DateDiff2 take leap years into account?

    • ferisoft

      Unfortunately no, I would advice on using method 1, since 2 is quite messier, but I can update it to support leap years if you are interested in the implementation?

      • Ciprian Radu

        that’s ok, I just thought I’m missing something in method 2. I don’t really need a timeago implementation in my projects yet so you don’t have to waste your time on this.