This document is available on the Internet at: http://urbanmainframe.com/folders/blog/20040217/folders/blog/20040217/
|
Tags:
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.
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.