PHP Developers Network

A community of PHP developers offering assistance, advice, discussion, and friendship.
 
Loading
It is currently Sun Jul 21, 2019 2:31 am

All times are UTC - 5 hours




Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Tue Feb 23, 2010 1:03 am 
Offline
Tranquility In Moderation
User avatar

Joined: Sun Feb 06, 2005 8:18 pm
Posts: 5001
Location: Indiana
Function:

Syntax: [ Download ] [ Hide ]
function time_passed($timestamp){
    //type cast, current time, difference in timestamps
    $timestamp      = (int) $timestamp;
    $current_time   = time();
    $diff           = $current_time - $timestamp;
   
    //intervals in seconds
    $intervals      = array (
        'year' => 31556926, 'month' => 2629744, 'week' => 604800, 'day' => 86400, 'hour' => 3600, 'minute'=> 60
    );
   
    //now we just find the difference
    if ($diff == 0)
    {
        return 'just now';
    }   

    if ($diff < 60)
    {
        return $diff == 1 ? $diff . ' second ago' : $diff . ' seconds ago';
    }       

    if ($diff >= 60 && $diff < $intervals['hour'])
    {
        $diff = floor($diff/$intervals['minute']);
        return $diff == 1 ? $diff . ' minute ago' : $diff . ' minutes ago';
    }       

    if ($diff >= $intervals['hour'] && $diff < $intervals['day'])
    {
        $diff = floor($diff/$intervals['hour']);
        return $diff == 1 ? $diff . ' hour ago' : $diff . ' hours ago';
    }   

    if ($diff >= $intervals['day'] && $diff < $intervals['week'])
    {
        $diff = floor($diff/$intervals['day']);
        return $diff == 1 ? $diff . ' day ago' : $diff . ' days ago';
    }   

    if ($diff >= $intervals['week'] && $diff < $intervals['month'])
    {
        $diff = floor($diff/$intervals['week']);
        return $diff == 1 ? $diff . ' week ago' : $diff . ' weeks ago';
    }   

    if ($diff >= $intervals['month'] && $diff < $intervals['year'])
    {
        $diff = floor($diff/$intervals['month']);
        return $diff == 1 ? $diff . ' month ago' : $diff . ' months ago';
    }   

    if ($diff >= $intervals['year'])
    {
        $diff = floor($diff/$intervals['year']);
        return $diff == 1 ? $diff . ' year ago' : $diff . ' years ago';
    }
}


Test:
Syntax: [ Download ] [ Hide ]
function test($ts){
    echo time_passed($ts) . '<br />';
} 
test(time());
test(time()-33);
test(time()-(60*17));
test(time()-((60*60*2) + (60*55)));
test(time()-(60*60*3+60));
test(strtotime('-1 day'));
test(strtotime('-13 days'));
test(strtotime('-25 days'));
test(strtotime('July 23 2009'));
test(strtotime('November 18 1999'));
test(strtotime('March 1 1937'));


Results:
Syntax: [ Download ] [ Hide ]
just now33
seconds ago
17 minutes ago
2 hours ago
3 hours ago
1 day ago
1 week ago
3 weeks ago
7 months ago
10 years ago
72 years ago
 


It works well for my purposes, but it feels ugly with all the if{}s. But I can't think of a better way to do it. Approximate timing is OK with me, I don't need to factor in leap years to make the months count exactly correct. But I did use accurate seconds for seconds in a month and seconds in a year.

_________________
- A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.


Last edited by Weirdan on Thu Dec 02, 2010 2:43 am, edited 1 time in total.
fixed formatting


Top
 Profile  
 
PostPosted: Tue Feb 23, 2010 2:17 am 
Offline
Site Administrator
User avatar

Joined: Sun May 19, 2002 10:24 pm
Posts: 6887
Nice!

You may want to look into using comparison operators in a switch. That would clean it up a little.

http://www.php.net/manual/en/control-st ... .php#93342

Also: use braces on all your if statements!

_________________
Image


Top
 Profile  
 
PostPosted: Tue Feb 23, 2010 2:39 am 
Offline
The Ninja Space Mod
User avatar

Joined: Fri Aug 05, 2005 1:53 pm
Posts: 6424
Location: Paradise, CA
I agree with astions. Always use braces with your if statements. And if you absolutely MUST leave out the braces, put it all on one line:

Syntax: [ Download ] [ Hide ]
if ($something) $foo = "bar";


Obviously this is a question of taste, so leave it if you want, but I think most developers prefer that you not do that.

Also, I wrote a view helper with similar functionality a while back. I thought you might be able to take some inspiration from it. Or maybe not. Either way, here it is:

Syntax: [ Download ] [ Hide ]
<?php
/**
 * Zend View Helper for "humanizing" dates and numbers.
 *
 * @package ImpSoft
 * @copyright Luke Visinoni (luke.visinoni@gmail.com)
 * @author Luke Visinoni (luke.visinoni@gmail.com)
 * @license GNU Lesser General Public License
 */

class ImpSoft_View_Helper_Humanize { 
    const DAYFORMAT = 'M d, Y';
    const TIMEFORMAT = 'g:ia';
    const DATEFORMAT = 'm/d/Y h:i:sa';   

    public function humanize() {
            return $this;
    }
    /**
     * Converts an integer to its ordinal as a string. 1 is '1st', 2 is '2nd', etc.
     * @todo Optionally humanize number as well and default to true. 1000 is 1,000th, etc.
     */

    public function ordinal($value) {
        $value = (integer) $value;
        $ord = array('th','st','nd','rd','th','th','th','th','th','th');
        // special cases
        if (in_array($value % 100, array(11, 12, 13))) return sprintf("%d%s", $value, $ord[0]);
           return sprintf('%d%s', $value, $ord[$value % 10]);
    }
    /**
     * Converts a large integer to a friendly text representation. Works best for
     * numbers over 1 million. For example, 1000000 becomes '1.0 million', 1200000
     * becomes '1.2 million' and '1200000000' becomes '1.2 billion'.
     *
     * For now, don't send numbers larger than 2 billion to it.
     */

    public function intword($value) {
        $value = (integer) $value;
        if ($value < 1000000) {
            return $value;
        }
        if ($value < 1000000000) {
            $new_value = $value / 1000000.0;
            return sprintf("%.1f million", $new_value);
        }
        if ($value < 1000000000000) {
            $new_value = $value / 1000000000.0;
            return sprintf("%.1f billon", $new_value);
        }
        /** PHP doesn't support numbers this large without an extension.
        if ($value < 1000000000000000) { 
           $new_value = $value / 1000000000000.0;
           return sprintf("%.1f trillion", $new_value);
        }
        */

        return $value;
     }
    /**
     * Returns a "humanized" day - today, tomorrow, yesterday if relevant,
     * otherwise it returns the date in $format format
     *
     * I am sure this is not the best way to do this, but it works
     */

    public function naturalDay($timestamp = null, $format = null) {
        if (is_null($timestamp)) $timestamp = time();
        if (is_null($format)) $format = self::DAYFORMAT;
        $oneday = 60*60*24;
        $today = strtotime('today');
        $tomorrow = $today + $oneday;
        $yesterday = $today - $oneday;
        // if time is 12:00 yesterday or more
        if ($timestamp >= $yesterday) {
            // if time is less than 12:00 the day after tomorrow
            if ($timestamp < $tomorrow + $oneday && $timestamp > $today) {
                // if time is less than 12:00 tomorrow
                if ($timestamp < $tomorrow) {
                    return 'today';
                }
                return 'tomorrow';
            }
            return 'yesterday';
        }
       
        return date($format, $timestamp);
    }
    /**
     * Returns a "humanized" time - so, if entry was today, it will say "about 16 minutes ago", "about 8 hours ago",
     * but if it isn't it will return the time formatted in $format format
     *
     * I am sure this is not the best way to do this, but it works
     */

    public function naturalTime($timestamp = null, $format = null) {
        if (is_null($timestamp)) $timestamp = time();
        if (is_null($format)) $format = self::TIMEFORMAT;
        $now = time();
        $hour = 60*60;
        if ($this->naturalDay($timestamp, $format) == 'today') {
            $hourago = $now - $hour;
            $hourfromnow = $now + $hour;
            // if timestamp passed in was after an hour ago...
            if ($timestamp > $hourago) {
                // if timestamp passed in is in the future...
                if ($timestamp > $now) {
                    // return how many minutes from now
                    $seconds = $timestamp - $now;
                    $minutes = (integer) round($seconds/60);
                    // if more than 60 minutes ago, report in hours
                    if ($minutes > 60) {
                        $hours = round($minutes/60);
                        return "in about $hours hours";
                    }
                    // if it got rounded down to zero, or it was one, report one
                    if (!$minutes || $minutes === 1) return "just now";
                    return "in about $minutes minutes";
                }
                // return how many minutes from now
                $seconds = $now - $timestamp;
                $minutes = (integer) round($seconds/60);
                // if it got rounded down to zero, or it was one, report one
                if (!$minutes || $minutes === 1) return "just now";
                return "about $minutes minutes ago";
            }
        }

        return date($format, $timestamp);
    }
    /**
     * For now, this will only convert numbers 0-9
     * @todo Convert a number to it's spelled-out version. For instance, 1 becomes one, 307 becomes
     * three hundred seven, 28 becomes twenty-eight.
     */

    public function strNum($value) {
        $int = (integer) $value;
        $numbers = array('zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine');
        if (isset($numbers[$value])) return $numbers[$value];
        return $value;
    }
 }


It is used like this:

Syntax: [ Download ] [ Hide ]
Posted on <?= $this->humanize()->naturalDay($this->post->datetime) ?> at <?= $this->humanize()->naturalTime($this->post->datetime) ?>


Last edited by Weirdan on Thu Dec 02, 2010 2:58 am, edited 1 time in total.
fixed formatting


Top
 Profile  
 
PostPosted: Tue Feb 23, 2010 6:06 am 
Offline
DevNet Master
User avatar

Joined: Wed Jun 27, 2007 9:44 am
Posts: 4313
Location: Sofia, Bulgaria
I'd use a lookup table instead of using control statements:
Syntax: [ Download ] [ Hide ]
function time_passed($timestamp)
{
    $diff = time() - (int)$timestamp;

    if ($diff == 0) 
         return 'just now';

    $intervals = array
    (
        1                   => array('year',    31556926),
        $diff < 31556926    => array('month',   2628000),
        $diff < 2629744     => array('week',    604800),
        $diff < 604800      => array('day',     86400),
        $diff < 86400       => array('hour',    3600),
        $diff < 3600        => array('minute',  60),
        $diff < 60          => array('second',  1)
    );

     $value = floor($diff/$intervals[1][1]);
     return $value.' '.$intervals[1][0].($value > 1 ? 's' : '').' ago';
}


It's somehow more "config" like :)

_________________
There are 10 types of people in this world, those who understand binary and those who don't


Last edited by VladSun on Tue Mar 30, 2010 2:27 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Tue Feb 23, 2010 3:00 pm 
Offline
Tranquility In Moderation
User avatar

Joined: Sun Feb 06, 2005 8:18 pm
Posts: 5001
Location: Indiana
You know what's funny, I ALWAYS use braces on everything, I just didn't for this post so the function didn't look so long LOL! Talk about irony.

@luke, that function is wayyyy cool! I can see me doing something like that, using yours as inspiration.

@vladsun, that is badass!! I was thinking along the lines of a lookup, but all my mind could come up with was using in_array() and range() and that would generate huuuuuuge arrays

I'm not even sure how your lookup table works from looking at the code, but dang that is cool. I'm going to play with it and see if I can figure it out.

_________________
- A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.


Top
 Profile  
 
PostPosted: Tue Feb 23, 2010 5:00 pm 
Offline
DevNet Master
User avatar

Joined: Wed Jun 27, 2007 9:44 am
Posts: 4313
Location: Sofia, Bulgaria
We had a "solving-intervals-match" topic recently - viewtopic.php?f=1&t=112042
Take a look at it.

_________________
There are 10 types of people in this world, those who understand binary and those who don't


Top
 Profile  
 
PostPosted: Wed Feb 24, 2010 5:16 pm 
Offline
Site Administrator
User avatar

Joined: Sun May 19, 2002 10:24 pm
Posts: 6887
Yeah the lookup table is awesome. I didn't realize you could do that. I will definitely start using that in my codebases.

_________________
Image


Top
 Profile  
 
PostPosted: Wed Feb 24, 2010 6:08 pm 
Offline
DevNet Master
User avatar

Joined: Wed Jun 27, 2007 9:44 am
Posts: 4313
Location: Sofia, Bulgaria
Thanks for the flowers guys :)

_________________
There are 10 types of people in this world, those who understand binary and those who don't


Top
 Profile  
 
PostPosted: Wed Feb 24, 2010 7:14 pm 
Offline
DevNet Resident
User avatar

Joined: Thu May 14, 2009 3:00 pm
Posts: 1011
Location: HellCity, Bulgaria


Top
 Profile  
 
PostPosted: Thu Feb 25, 2010 2:23 am 
Offline
DevNet Master
User avatar

Joined: Wed Jun 27, 2007 9:44 am
Posts: 4313
Location: Sofia, Bulgaria
Heh, an offer one can not refuse :)
Thanks, buddy :drunk:

_________________
There are 10 types of people in this world, those who understand binary and those who don't


Top
 Profile  
 
PostPosted: Sat Mar 06, 2010 12:37 pm 
Offline
DevNet Master

Joined: Wed Feb 11, 2004 4:23 pm
Posts: 4872
Location: Palm beach, Florida
So much better then storing all the columns if saving state to a database too (bitwise lookups). Generally would be done after the initial implementation with if statements, as a refactoring. Pretty interesting application of it vlad


Top
 Profile  
 
PostPosted: Sun Mar 07, 2010 5:22 pm 
Offline
DevNet Master
User avatar

Joined: Wed Jun 27, 2007 9:44 am
Posts: 4313
Location: Sofia, Bulgaria
Reference:
viewtopic.php?f=1&t=112042&p=597424#p597424

_________________
There are 10 types of people in this world, those who understand binary and those who don't


Top
 Profile  
 
PostPosted: Mon Mar 08, 2010 3:55 am 
Offline
Tranquility In Moderation
User avatar

Joined: Sun Feb 06, 2005 8:18 pm
Posts: 5001
Location: Indiana
I would alter my function to include the lookup table instead of the if's ;) But then it would be Vladsun's function and not mine, and I'm not about stealing someone's thunder :P

Readers can choose which they want to use.

_________________
- A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.


Top
 Profile  
 
PostPosted: Mon Mar 08, 2010 4:27 am 
Offline
Tranquility In Moderation
User avatar

Joined: Sun Feb 06, 2005 8:18 pm
Posts: 5001
Location: Indiana
I did a little speed test to see if using a lookup table would be faster or slower than using conditional if{}'s. On a heavy trafficked place like a forum it may be the deciding factor for which function to use.

I realize the following test does not determine the true speed of each function, but it should provide a somewhat accurate correlation of speed comparing the two functions.

code
Syntax: [ Download ] [ Hide ]
<?php 
class timer{
    private $start;
    private $end;
   
    public function start()
    {
        $this->start = microtime(true);
    }
   
    public function end()
    {
        $this->end = microtime(true);
    }
   
    public function get_time()
    {
        return ($this->end - $this->start;
    }
   
    public function clear()
    {
        $this->start = null;
        $this->end = null;
    }
} 

function a($timestamp){
     //type cast, current time, difference in timestamps
     $timestamp      = (int) $timestamp;
     $current_time   = time();
     $diff           = $current_time - $timestamp;
     
     //intervals in seconds
     $intervals      = array (
         'year' => 31556926, 'month' => 2629744, 'week' => 604800, 'day' => 86400, 'hour' => 3600, 'minute'=> 60
     );
     
    //now we just find the difference
     if ($diff == 0)
     {
         return 'just now';
     }
     
     if ($diff < 60)
     {
         return $diff == 1 ? $diff . ' second ago' : $diff . ' seconds ago';
     }
     
    
     if ($diff >= 60 && $diff < $intervals['hour'])
     {
         $diff = floor($diff/$intervals['minute']);
         return $diff == 1 ? $diff . ' minute ago' : $diff . ' minutes ago';
     }
     
    
     if ($diff >= $intervals['hour'] && $diff < $intervals['day'])
     {
         $diff = floor($diff/$intervals['hour']);
         return $diff == 1 ? $diff . ' hour ago' : $diff . ' hours ago';
     }
   
     if ($diff >= $intervals['day'] && $diff < $intervals['week'])
     {
         $diff = floor($diff/$intervals['day']);
         return $diff == 1 ? $diff . ' day ago' : $diff . ' days ago';
     }
     
     if ($diff >= $intervals['week'] && $diff < $intervals['month'])
     {
         $diff = floor($diff/$intervals['week']);
         return $diff == 1 ? $diff . ' week ago' : $diff . ' weeks ago';
     }
   
     if ($diff >= $intervals['month'] && $diff < $intervals['year'])
     {
         $diff = floor($diff/$intervals['month']);
         return $diff == 1 ? $diff . ' month ago' : $diff . ' months ago';
     }
   
     if ($diff >= $intervals['year'])
     {
         $diff = floor($diff/$intervals['year']);
         return $diff == 1 ? $diff . ' year ago' : $diff . ' years ago';
     }
} 

function b($timestamp){
     $diff = time() - (int)$timestamp;
       if ($diff == 0)
         return 'just now';
       $intervals = array     (
         1                   => array('year',    31556926),
         $diff < 31556926    => array('month',   2628000),
         $diff < 2629744     => array('week',    604800),
         $diff < 604800      => array('day',     86400),
         $diff < 86400       => array('hour',    3600),
         $diff < 3600        => array('minute',  60),
         $diff < 60          => array('second',  1)
     );
     $value = floor($diff/$intervals[1][1]);
     return $value.' '.$intervals[1][0].($value > 1 ? 's' : '').' ago';
} //time values for comparison

$s = time()-10;
$m = time()-70;
$h = time()-60*60*2;
$d = strtotime('-2 days');
$w = strtotime('-3 weeks');
$n = strtotime('-4 months');
$y = strtotime('-5 years');  

//function a$timer = new timer();
$timer->start();
for ($i=0; $i<10000; $i++){
    a($s); //seconds
    a($m); //minutes
    a($h); //hours
    a($d); //day
    a($w); //week
    a($n); //month
    a($y); //year
} 

$timer->end();

echo 'Function a(), 10,000 iterations using one date in each time range: ' . $timer->get_time() . ' seconds' . "\n";
$timer->clear();

//function b
$timer->start();
for ($i=0; $i<10000; $i++){
    b($s); //seconds
    b($m); //minutes
    b($h); //hours
    b($d); //day
    b($w); //week
    b($n); //month
    b($y); //year
} 

$timer->end();
echo 'Function b(), 10,000 iterations using one date in each time range: ' . $timer->get_time() . ' seconds' . "\n";
 


result
Syntax: [ Download ] [ Hide ]
Function a(), 10,000 iterations using one date in each time range: 0.595626831055 seconds
Function b(), 10,000 iterations using one date in each time range: 0.972527980804 seconds


So if we want to break that down on a per call function

function a
Syntax: [ Download ] [ Hide ]
0.595626831055 / (10,000 iterations * 7 function calls per iteration) = 0.00000850895473


function b
Syntax: [ Download ] [ Hide ]
0.972527980804 / (10,000 iterations * 7 function calls per iteration) = 0.00001389325687


The lookup table does appear to be a tiiiiiiiiny bit slower, nothing big though.

Can you explain why vladsun? Does the lookup table have to evaluate every possible circumstance before it can return, rather than the if{}'s returning immediately once the value is found?

_________________
- A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.


Last edited by Weirdan on Thu Dec 02, 2010 3:07 am, edited 1 time in total.
fixed formatting


Top
 Profile  
 
PostPosted: Mon Mar 08, 2010 4:37 am 
Offline
DevNet Master
User avatar

Joined: Wed Jun 27, 2007 9:44 am
Posts: 4313
Location: Sofia, Bulgaria

_________________
There are 10 types of people in this world, those who understand binary and those who don't


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next

All times are UTC - 5 hours


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
Powered by phpBB® Forum Software © phpBB Group