flag of the United Kingdom
URBAN
Mainframe

Function: time_since()

Date:  Tue, 17th-Feb-2004prevnext

Tags: CMS, Open Source, Perl, Programming, Urban Mainframe, Website Development

I have taken Natalie Downe's "time_since" function, recoded it in Perl and made my "Last Modified" time-stamp a little more user-friendly...

I first spotted this function in use on Dunstan's blog (on the "b-roll" section) and I immediately saw the value of it.

The script takes the absolute dates we are familiar with (eg: 17-Feb-2004) and expresses them in a relative form - like "3 days, 7 hours ago". The function does a great job of breaking down the usual problems with dates on the web including: localisation, timezone differences, format differences, etc.

Dunstan explains the benefits better than I could.

The Code

sub time_since {

    # With thanks to Natalie Downe (http://blog.natbat.co.uk/archive/2003/Jun/14/time_since) for the inspiration...

    # Initialise...
    use Date::Calc qw(Delta_DHMS Today_and_Now Date_to_Text_Long check_date check_time);
    my (%temp, %elapsed); my @map = ('year', 'month', 'week', 'day', 'hour', 'minute'); 

    # Validation...
    unless ($_[0] =~ /d{14}/) { return ('[invalid timestamp]'); }
    unless (check_date (substr ($_[0], 0, 4), substr ($_[0], 4, 2), substr ($_[0], 6, 2)) ) { return ('[invalid date]'); }
    unless (check_time (substr ($_[0], 8, 2), substr ($_[0], 10, 2), substr ($_[0], 12, 2)) ) { return ('[invalid time]'); }

    # Get the "time since" (%elapsed)...
    ($elapsed{'day'}, $elapsed{'hour'}, $elapsed{'minute'}, $elapsed{'second'}) = Delta_DHMS (substr ($_[0], 0, 4), substr ($_[0], 4, 2), substr ($_[0], 6, 2), substr ($_[0], 8, 2), substr ($_[0], 10, 2), substr ($_[0], 12, 2), Today_and_Now());

    # Do the math...
    $temp{'total_minutes'} = $elapsed{'minute'} + ($elapsed{'hour'} * 60) + (($elapsed{'day'} * 24) * 60);
    $elapsed{'year'} = int ($elapsed{'day'} / 365); $elapsed{'day'} -= ($elapsed{'year'} * 365);
    $elapsed{'month'} = int ($elapsed{'day'} / 30); $elapsed{'day'} -= ($elapsed{'month'} * 30);
    $elapsed{'week'} = int ($elapsed{'day'} / 7);  $elapsed{'day'} -= ($elapsed{'week'} * 7); 
    if ($temp{'total_minutes'} > (60 * 24)) { undef $elapsed{'minute'}; } 
    if ($temp{'total_minutes'} > (60 * 24 * 7)) { undef $elapsed{'hour'}; } 
    if ($temp{'total_minutes'} > (60 * 24 * 30)) { undef $elapsed{'day'}; } 
    if ($temp{'total_minutes'} > (60 * 24 * 30 * 6)) { undef $elapsed{'week'}; } 
    if ($temp{'total_minutes'} > (60 * 24 * 365 * 5)) { undef $elapsed{'month'}; }

    # Build the output string... 
    foreach my $element (@map) { 
        $temp{'grammar'} = ($elapsed{$element} == 1) ? '' : 's'; 
        $temp{'time_since'} .= ($elapsed{$element}) ? $elapsed{$element} . ' ' . $element . $temp{'grammar'} . ', ' : ''; 
    } 
    $temp{'time_since'} =~ s/, $///; 
    $temp{'time_since'} = 'less than 1 minute' unless ($temp{'time_since'}); 
    $temp{'time_since'} = '<span title="' . Date_to_Text_Long (substr ($_[0], 0, 4), substr ($_[0], 4, 2), substr ($_[0], 6, 2)) . ' @ ' . substr ($_[0], 8, 2) . ':' . substr ($_[0], 10, 2) . '">' . $temp{'time_since'} . '</span>';

    # Return it... 
    return ($temp{'time_since'});

}

The function takes a 14-digit time-stamp in the format: yyyymmddhhmmss as its input. Example: my $elapsed_time = &time_since ('20040217064500');

The function uses Steffen Beyer's "Date::Calc" library which is included with many Perl installations. As with all my code, it runs under Perl's "strict" mode and is compatible with "mod_perl".

So far, I have only used this function on the "Last Modified" time-stamp on my pages and the forum / comment summaries on the front page. But, depending on feedback, I might deploy it elsewhere - obvious locations being on comment pages and forum posts. What do you think?

Natalie's original script is available from her own weblog.


UPDATE (18-Feb-2004): Added validation of the timestamp input. Thanks Stas.

You can comment on this entry, or read what others have written (17 comments).


W3C VALIDATE XHTML
W3C VALIDATE CSS