开发者

Relative date formatting always outputs "3 hours ago"

I am calculating the date interval between a specified date and the current date using php. I do this so I can print out social friendly timestamps like A few mins ago and 2 hours ago

When I come to the hours part I get something behaving real funny in php. Below is the full working code but when you replace the hours part with this code it always prints out 3 hours.

Defined constant DATE

// The current date timestamp
define('DATE', time());

Buggy code here:

//Only the hours part that' doing something weird
case ($interval >= 3600 && $interval < 86400) :
            $return = ( date('H', $interval) < 2)
                ? (int)date('H', $interval) . ' hour ago'
                : (int)date('H', $interval) . ' hours ago';
            break;

It breaks when the specified date, lets say in this case, create date is just over an hour ago thus resulting in an interval equal to 3660 seconds. It seems like the date method call date('H', 3660) results in 03. Shouldn't it result in 01? it is after all just over an hour.

Working code here:

public static function getTimeInterval($date)
{
    $interval = D开发者_StackOverflow社区ATE - $date;

    $return = '';

    switch ( $interval )
    {
        case ($interval <= 60) :
            $return = 'a few secs ago';
            break;

        case ($interval > 60 && $interval < 3600) :
            $return = (int)date('i', $interval) . ' mins ago';
            break;

        case ($interval >= 3600 && $interval < 86400) :
            $return = ( abs((date('G', DATE) - date('G', $date))) < 2)
                ? abs((date('G', DATE) - date('G', $date))) . ' hour ago'
                : abs((date('G', DATE) - date('G', $date))) . ' hours ago';
            break;

        case ($interval >= 86400 && $interval < 604800) :
            $return = ( (int)date('j', $interval) === 1)
                ? (int)date('j', $interval) . ' day ago'
                : (int)date('j', $interval) . ' days ago';
            break;

        case ($interval > 604800 && $interval <= 2592000) :
            $return = 'A few weeks ago';
            break;
        case ($interval > 2592000) :
            $return = date('n', $interval) . ' months ago';
            break;
        case ($interval > 31536000) :
            $return = 'Over a year ago';
            break;

    }

    return $return;

}


result of date() depends on your timezone. You can change this behavior by setting timezone manually by date_default_timezone_set()


Check this code, it works good:

function do_plurals($nb, $str)
{
    return $nb > 1 ? $str . 's' : $str;
}


function dates_interval_text(DateTime $start, DateTime $end)
{
    $interval = $end->diff($start);

    $format = array();
    if ($interval->y !== 0)
    {
        $format[] = "%y " . do_plurals($interval->y, "year");
    }
    if ($interval->m !== 0)
    {
        $format[] = "%m " . do_plurals($interval->m, "month");
    }
    if ($interval->d !== 0)
    {
        $format[] = "%d " . do_plurals($interval->d, "day");
    }
    if ($interval->h !== 0)
    {
        $format[] = "%h " . do_plurals($interval->h, "hour");
    }
    if ($interval->i !== 0)
    {
        $format[] = "%i " . do_plurals($interval->i, "minute");
    }
    if (!count($format))
    {
        return "less than a minute ago";
    }

    // We use the two biggest parts
    if (count($format) > 1)
    {
        $format = array_shift($format) . " and " . array_shift($format);
    }
    else
    {
        $format = array_pop($format);
    }

    // Prepend 'since ' or whatever you like
    return $interval->format($format) . ' ago';
}

It produces results like 2 hours and 3 minutes ago


date('H', 3660) results in 01 for me. Perhaps the timezone that your in is being applied. That could be at the local level (application) or global level (php config). Maybe even it's your OS. I'm sorry I can't be of much more help with which PHP settings to look for and whatnot.


The second argument to date is not a duration, it's a timestamp. date answers the question "N seconds after 01/01/1970 00:00 UTC, what time was it?" and the answer to this question is given in your time zone, so your time zone offset is added to the answer.

Use date('Z') to find your time zone offset, in seconds. Or, even better, use floor($time/3600) to compute the hours.


Your code is running fine. There is an error in the way you are calling it. It runs pretty well when $date is passed as a unix timestamp eg $date = time() - 1*60*60 ( for 1 hour ago ).

http://codepad.org/HS25qThK

$aboutHourAgo = time() - 2.5*60*60;   // 2 and half hours ago
var_dump(getTimeInterval($aboutHourAgo));


My first question would be, where are you getting the time/timestamp/datetime from?

At our company, we run a few servers in Germany and the applications hosted on them are for Pakistan (GMT+5). we use this function that takes a Unix Timestamp to return a "Human readable" date time: -

/**
 * @desc Converts UNIX Time into human readable time like '1 hour', 3 weeks', etc.
 * @param number $timestamp
 * @return string
 */
function readable_time($timestamp, $num_times = 2)  {
    // this returns human readable time when it was uploaded
    $times = array (
    31536000 => 'year', 2592000 => 'month',
    604800 => 'week', 86400 => 'day',
    3600 => 'hour', 60 => 'minute', 1 => 'second'
    );
    $now = time ();
    $secs = $now - $timestamp;
    // Fix so that something is always displayed
    if ($secs == 0) $secs = 1;
    $count = 0; $time = '';
    foreach ( $times as $key => $value )    {
        if ($secs >= $key)  {
            // time found
            $s = '';
            $time .= floor ( $secs / $key );
            if ((floor ( $secs / $key ) != 1))  $s = 's';
            $time .= ' ' . $value . $s;
            $count ++;
            $secs = $secs % $key;
            if ($count > $num_times - 1 || $secs == 0) break;
            else $time .= ', ';
        }
    }
    return ($time != "") ? $time." ago" : "now";
}

By modifying the $num_times variable, we can get something like "4 months, 3 days, 2 hours, 3 minutes, 15 seconds ago" (setting variable to 5, in this example).

Logically, the timestamp comes from a database - in our case MySQL - so we use another function that takes a MySQL timestamp and converts it into a Unix timestamp: -

/**
 * @desc Converts UNIX Time into human readable time like '1 hour', 3 weeks', etc.
 * @param number $timestamp
 * @return string
 */
function RelativeTime($timestamp)   {
    $difference = time() - $timestamp;
    $periods = array("sec", "min", "hour", "day", "week", "month", "years", "decade");
    $lengths = array("60", "60", "24", "7", "4.35", "12", "10");
    if ($difference > 0)    { // this was in the past
        $ending = "ago";
    }   else    { // this was in the future
        $difference = -$difference;
        $ending = "to go";
    }
    for($j = 0; $difference >= $lengths[$j]; $j++) $difference /= $lengths[$j];
    $difference = round($difference);
    if($difference != 1) $periods[$j].= "s";
    $text = "$difference $periods[$j] $ending";
    return $text;
}

Good Luck!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜