PHP: producing relative date/time from timestamps
This function gives you "1 hour ago" or "Tomorrow" like results between 'now' and 'specific timestamp'.
function time2str($ts)
{
if(!ctype_digit($ts))
$ts = strtotime($ts);
$diff = time() - $ts;
if($diff == 0)
return 'now';
elseif($diff > 0)
{
$day_diff = floor($diff / 86400);
if($day_diff == 0)
{
if($diff < 60) return 'just now';
if($diff < 120) return '1 minute ago';
if($diff < 3600) return floor($diff / 60) . ' minutes ago';
if($diff < 7200) return '1 hour ago';
if($diff < 86400) return floor($diff / 3600) . ' hours ago';
}
if($day_diff == 1) return 'Yesterday';
if($day_diff < 7) return $day_diff . ' days ago';
if($day_diff < 31) return ceil($day_diff / 7) . ' weeks ago';
if($day_diff < 60) return 'last month';
return date('F Y', $ts);
}
else
{
$diff = abs($diff);
$day_diff = floor($diff / 86400);
if($day_diff == 0)
{
if($diff < 120) return 'in a minute';
if($diff < 3600) return 'in ' . floor($diff / 60) . ' minutes';
if($diff < 7200) return 'in an hour';
if($diff < 86400) return 'in ' . floor($diff / 3600) . ' hours';
}
if($day_diff == 1) return 'Tomorrow';
if($day_diff < 4) return date('l', $ts);
if($day_diff < 7 + (7 - date('w'))) return 'next week';
if(ceil($day_diff / 7) < 4) return 'in ' . ceil($day_diff / 7) . ' weeks';
if(date('n', $ts) == date('n') + 1) return 'next month';
return date('F Y', $ts);
}
}
function relativeTime($time) {
$d[0] = array(1,"second");
$d[1] = array(60,"minute");
$d[2] = array(3600,"hour");
$d[3] = array(86400,"day");
$d[4] = array(604800,"week");
$d[5] = array(2592000,"month");
$d[6] = array(31104000,"year");
$w = array();
$return = "";
$now = time();
$diff = ($now-$time);
$secondsLeft = $diff;
for($i=6;$i>-1;$i--)
{
$w[$i] = intval($secondsLeft/$d[$i][0]);
$secondsLeft -= ($w[$i]*$d[$i][0]);
if($w[$i]!=0)
{
$return.= abs($w[$i]) . " " . $d[$i][1] . (($w[$i]>1)?'s':'') ." ";
}
}
$return .= ($diff>0)?"ago":"left";
return $return;
}
Usage:
echo relativeTime((time()-256));
4 minutes 16 seconds ago
Here is what I've written. Displays a past date relative to today's date.
/**
* @param $date integer of unixtimestamp format, not actual date type
* @return string
*/
function zdateRelative($date)
{
$now = time();
$diff = $now - $date;
if ($diff < 60){
return sprintf($diff > 1 ? '%s seconds ago' : 'a second ago', $diff);
}
$diff = floor($diff/60);
if ($diff < 60){
return sprintf($diff > 1 ? '%s minutes ago' : 'one minute ago', $diff);
}
$diff = floor($diff/60);
if ($diff < 24){
return sprintf($diff > 1 ? '%s hours ago' : 'an hour ago', $diff);
}
$diff = floor($diff/24);
if ($diff < 7){
return sprintf($diff > 1 ? '%s days ago' : 'yesterday', $diff);
}
if ($diff < 30)
{
$diff = floor($diff / 7);
return sprintf($diff > 1 ? '%s weeks ago' : 'one week ago', $diff);
}
$diff = floor($diff/30);
if ($diff < 12){
return sprintf($diff > 1 ? '%s months ago' : 'last month', $diff);
}
$diff = date('Y', $now) - date('Y', $date);
return sprintf($diff > 1 ? '%s years ago' : 'last year', $diff);
}
I love the relativeTime function by xdebug. Problem is I needed it to have some granularity.
In other words stop at seconds or minutes if I decide. So now,
echo fTime(strtotime('-23 hours 5 minutes 55 seconds'),0);
would show,
23 hours, 5 minutes ago
Instead of
23 hours, 5 minutes, 55 seconds ago
I also wanted it to NOT go lower in the array if it reached one of the higher time amounts. So if it shows years, I only want to show years and months. So now,
echo fTime(strtotime('-1 year 2 months 3 weeks 4 days 16 hours 15 minutes 22 seconds'),0);
Would show
1 year, 2 months ago
Instead of
1 year, 2 months, 3 weeks, 4 days, 16 hours, 15 minutes, 22 seconds ago
The following code change did what I needed. Props go to xdebug first of course. Hopefully someone else might find it useful:
function fTime($time, $gran=-1) {
$d[0] = array(1,"second");
$d[1] = array(60,"minute");
$d[2] = array(3600,"hour");
$d[3] = array(86400,"day");
$d[4] = array(604800,"week");
$d[5] = array(2592000,"month");
$d[6] = array(31104000,"year");
$w = array();
$return = "";
$now = time();
$diff = ($now-$time);
$secondsLeft = $diff;
$stopat = 0;
for($i=6;$i>$gran;$i--)
{
$w[$i] = intval($secondsLeft/$d[$i][0]);
$secondsLeft -= ($w[$i]*$d[$i][0]);
if($w[$i]!=0)
{
$return.= abs($w[$i]) . " " . $d[$i][1] . (($w[$i]>1)?'s':'') ." ";
switch ($i) {
case 6: // shows years and months
if ($stopat==0) { $stopat=5; }
break;
case 5: // shows months and weeks
if ($stopat==0) { $stopat=4; }
break;
case 4: // shows weeks and days
if ($stopat==0) { $stopat=3; }
break;
case 3: // shows days and hours
if ($stopat==0) { $stopat=2; }
break;
case 2: // shows hours and minutes
if ($stopat==0) { $stopat=1; }
break;
case 1: // shows minutes and seconds if granularity is not set higher
break;
}
if ($i===$stopat) { break 0; }
}
}
$return .= ($diff>0)?"ago":"left";
return $return;
}
Marcus