Oct 28, 2008

Relative Times in PHP

While working on a side project I found myself in need of a function to produce relative times and dates--e.g. "3 minutes ago" instead of "October 28, 2008 12:18 AM". You'd think this should be easy to find in the age of Google, but I was quite disappointed. It turns out that there are some simple functions out there, but none that adequately take into account the new PHP 5 DateTime object (which avoids UNIX timestamps and therefore allows years prior to 1970 and after 2038), and is performant. So here's my solution for posterity:

/* Warning: this is a quick and dirty solution. I'm sure there
are some bugs or better ways of doing it. Hoever, feel free
to use it however you like. */

function is_leap_year($year) {
return ((($year % 100 == 0) && ($year % 400 == 0)) || ($year % 4 == 0));
}

/* for relative dates */
$month_lengths = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
$units = array("year", "month", "day", "hour", "minute", "second");
$unit_comp_max = array(12, 30, 24, 60, 60);

function relative_time($datetime){
global $month_lengths;
global $units;
global $unit_component_max;

$a = date_parse($datetime);
$b = date_parse(date("r"));

for ($i=0; $i $val = 0;
$unit = $units[$i];
$val = $b[$unit] - $a[$unit];
if ($val > 0) {
if ($a[$units[$i+1]] >= $b[$units[$i+1]]) {
$val = $val - 1;
if ($unit == "month") {
$b['day'] = $b['day'] +$month_lengths[$a['month']];
if ($a['month'] == 2 && is_leap_year($a['year'])) {
$b['day'] = $b['day'] + 1;
}
} else {
$b[$units[$i+1]] = $b[$units[$i+1]] + $unit_comp_max[$i];
}
}
if ($val > 0) {
return $val . " " . $unit . (($val>1)? "s ago":" ago");
}
}
}

$val = $b['second'] - $a['second'];
if ($val > 0) {
return $val . " " . (($val>1)? "seconds ago":"second ago");
}

return "only moments ago";
}
Please let me know if you have a better solution, but this seems to work for me at the moment.

No comments: