summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorByron Jones <glob@mozilla.com>2015-05-27 05:57:29 +0200
committerByron Jones <glob@mozilla.com>2015-05-27 05:57:29 +0200
commit35aab8c392ac6ad404bb0d902cca6b50480072da (patch)
tree035f26e0c3151fbd088b07c6e8140ce6412c5f2e
parent087f74073fdc81ec5707f2989c99b34770e864eb (diff)
downloadbugzilla-35aab8c392ac6ad404bb0d902cca6b50480072da.tar.gz
bugzilla-35aab8c392ac6ad404bb0d902cca6b50480072da.tar.xz
Bug 1158010: provide a standard and simple way to render relative dates, in perl and javascript
-rw-r--r--Bugzilla/Util.pm70
-rw-r--r--extensions/BugModal/Config.pm8
-rw-r--r--extensions/BugModal/Extension.pm7
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/header.html.tmpl2
-rw-r--r--extensions/BugModal/web/bug_modal.js11
-rw-r--r--extensions/BugModal/web/time_ago.js142
-rw-r--r--extensions/MozReview/web/js/mozreview.js39
-rw-r--r--extensions/MyDashboard/lib/Queries.pm3
-rw-r--r--extensions/MyDashboard/lib/TimeAgo.pm179
-rw-r--r--extensions/RequestNagger/Extension.pm3
-rw-r--r--extensions/RequestNagger/lib/TimeAgo.pm186
-rw-r--r--extensions/UserProfile/Extension.pm3
-rw-r--r--extensions/UserProfile/lib/TimeAgo.pm179
-rw-r--r--js/util.js27
14 files changed, 114 insertions, 745 deletions
diff --git a/Bugzilla/Util.pm b/Bugzilla/Util.pm
index 67798d470..f2291a812 100644
--- a/Bugzilla/Util.pm
+++ b/Bugzilla/Util.pm
@@ -40,12 +40,13 @@ use base qw(Exporter);
validate_ip do_ssl_redirect_if_required use_attachbase
diff_arrays on_main_db
trim wrap_hard wrap_comment find_wrap_point
- format_time validate_date validate_time datetime_from
+ format_time validate_date validate_time datetime_from time_ago
file_mod_time is_7bit_clean
bz_crypt generate_random_password
validate_email_syntax clean_text
get_text template_var disable_utf8
- enable_utf8 detect_encoding email_filter);
+ enable_utf8 detect_encoding email_filter
+ round);
use Bugzilla::Constants;
use Bugzilla::RNG qw(irand);
@@ -61,6 +62,7 @@ use Scalar::Util qw(tainted blessed);
use Text::Wrap;
use Encode qw(encode decode resolve_alias);
use Encode::Guess;
+use POSIX qw(floor ceil);
sub trick_taint {
require Carp;
@@ -603,6 +605,30 @@ sub datetime_from {
return $dt;
}
+sub time_ago {
+ my ($param) = @_;
+ # DateTime object or seconds
+ my $ss = ref($param) ? time() - $param->epoch : $param;
+ my $mm = round($ss / 60);
+ my $hh = round($mm / 60);
+ my $dd = round($hh / 24);
+ my $mo = round($dd / 30);
+ my $yy = round($mo / 12);
+
+ return 'just now' if $ss < 10;
+ return $ss . ' seconds ago' if $ss < 45;
+ return 'a minute ago' if $ss < 90;
+ return $mm . ' minutes ago' if $mm < 45;
+ return 'an hour ago' if $mm < 90;
+ return $hh . ' hours ago' if $hh < 24;
+ return 'a day ago' if $hh < 36;
+ return $dd . ' days ago' if $dd < 30;
+ return 'a month ago' if $dd < 45;
+ return $mo . ' months ago' if $mo < 12;
+ return 'a year ago' if $mo < 18;
+ return $yy . ' years ago';
+}
+
sub file_mod_time {
my ($filename) = (@_);
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
@@ -850,6 +876,17 @@ sub _guess_iso {
return $encoding;
}
+# From Math::Round
+use constant ROUND_HALF => 0.50000000000008;
+sub round {
+ my @res = map {
+ $_ >= 0
+ ? floor($_ + ROUND_HALF)
+ : ceil($_ - ROUND_HALF);
+ } @_;
+ return (wantarray) ? @res : $res[0];
+}
+
1;
__END__
@@ -1161,8 +1198,15 @@ You can optionally specify a timezone for the returned date. If not
specified, defaults to the currently-logged-in user's timezone, or
the Bugzilla server's local timezone if there isn't a logged-in user.
-=back
+=item C<time_ago($datetime_object)>, C<time_ago($seconds)>
+
+Returns a concise representation of the time passed. eg. "11 months ago".
+Accepts either a DateTime object, which is assumed to be in the past, or
+seconds.
+
+
+=back
=head2 Files
@@ -1239,3 +1283,23 @@ if Bugzilla is currently using the shadowdb or not. Used like:
}
=back
+
+=head2 Math and Numbers
+
+=over
+
+=item C<round(@list)>
+
+Rounds the number(s) to the nearest integer. In scalar context, returns a
+single value; in list context, returns a list of values. Numbers that are
+halfway between two integers are rounded "to infinity"; i.e., positive values
+are rounded up (e.g., 2.5 becomes 3) and negative values down (e.g., -2.5
+becomes -3).
+
+=begin undocumented
+
+Lifted directly from Math::Round to avoid a new dependency for trivial code.
+
+=end undocumented
+
+=back
diff --git a/extensions/BugModal/Config.pm b/extensions/BugModal/Config.pm
index b4242753a..a60a99a7a 100644
--- a/extensions/BugModal/Config.pm
+++ b/extensions/BugModal/Config.pm
@@ -9,13 +9,7 @@ package Bugzilla::Extension::BugModal;
use strict;
use constant NAME => 'BugModal';
-use constant REQUIRED_MODULES => [
- {
- package => 'Time-Duration',
- module => 'Time::Duration',
- version => 0
- },
-];
+use constant REQUIRED_MODULES => [ ];
use constant OPTIONAL_MODULES => [ ];
__PACKAGE__->NAME;
diff --git a/extensions/BugModal/Extension.pm b/extensions/BugModal/Extension.pm
index 0bc96e730..f09a53425 100644
--- a/extensions/BugModal/Extension.pm
+++ b/extensions/BugModal/Extension.pm
@@ -17,10 +17,9 @@ use Bugzilla::Extension::BugModal::MonkeyPatches;
use Bugzilla::Extension::BugModal::Util qw(date_str_to_time);
use Bugzilla::Constants;
use Bugzilla::User::Setting;
-use Bugzilla::Util qw(trick_taint datetime_from html_quote);
+use Bugzilla::Util qw(trick_taint datetime_from html_quote time_ago);
use List::MoreUtils qw(any);
use Template::Stash;
-use Time::Duration qw(ago);
our $VERSION = '1';
@@ -55,7 +54,7 @@ sub template_after_create {
my ($self, $args) = @_;
my $context = $args->{template}->context;
- # wrapper around Time::Duration::ago()
+ # wrapper around time_ago()
$context->define_filter(
time_duration => sub {
my ($context) = @_;
@@ -63,7 +62,7 @@ sub template_after_create {
my ($timestamp) = @_;
my $datetime = datetime_from($timestamp)
// return $timestamp;
- return ago(abs(time() - $datetime->epoch));
+ return time_ago($datetime);
};
}, 1
);
diff --git a/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl
index 13ec7d567..13c146ed5 100644
--- a/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl
+++ b/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl
@@ -49,11 +49,11 @@
# assets
javascript_urls.push(
"extensions/ProdCompSearch/web/js/prod_comp_search.js",
- "extensions/BugModal/web/time_ago.js",
"extensions/BugModal/web/bug_modal.js",
"extensions/BugModal/web/ZeroClipboard/ZeroClipboard.min.js",
"js/field.js",
"js/comments.js",
+ "js/util.js"
);
jquery.push(
"datetimepicker",
diff --git a/extensions/BugModal/web/bug_modal.js b/extensions/BugModal/web/bug_modal.js
index 66e214ad6..32949828f 100644
--- a/extensions/BugModal/web/bug_modal.js
+++ b/extensions/BugModal/web/bug_modal.js
@@ -8,6 +8,17 @@
$(function() {
'use strict';
+ // update relative dates
+ window.setInterval(function() {
+ var now = Math.floor(new Date().getTime() / 1000);
+ $('.rel-time').each(function() {
+ $(this).text(timeAgo(now - $(this).data('time')));
+ });
+ $('.rel-time-title').each(function() {
+ $(this).attr('title', timeAgo(now - $(this).data('time')));
+ });
+ }, 60000);
+
// all keywords for autocompletion (lazy-loaded on edit)
var keywords = [];
diff --git a/extensions/BugModal/web/time_ago.js b/extensions/BugModal/web/time_ago.js
deleted file mode 100644
index 59f12692a..000000000
--- a/extensions/BugModal/web/time_ago.js
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * this is a port of perl's Time::Duration module, which has the following
- * license:
- *
- * Copyright 2006, Sean M. Burke C<sburke@cpan.org>, all rights reserved. This
- * program is free software; you can redistribute it and/or modify it under the
- * same terms as Perl itself.
- *
- * This program is distributed in the hope that it will be useful, but without
- * any warranty; without even the implied warranty of merchantability or
- * fitness for a particular purpose.
- */
-
-$(function() {
- 'use strict';
-
- function separate(seconds) {
- // breakdown of seconds into units, starting with the most significant
-
- var remainder = seconds;
- var tmp;
- var wheel = [];
-
- // years
- tmp = Math.floor(remainder / (365 * 24 * 60 * 60));
- wheel.push([ 'year', tmp, 1000000000 ]);
- remainder -= tmp * (365 * 24 * 60 * 60);
-
- // days
- tmp = Math.floor(remainder / (24 * 60 * 60));
- wheel.push([ 'day', tmp, 365 ]);
- remainder -= tmp * (24 * 60 * 60);
-
- // hours
- tmp = Math.floor(remainder / (60 * 60));
- wheel.push([ 'hour', tmp, 24 ]);
- remainder -= tmp * (60 * 60);
-
- // minutes
- tmp = Math.floor(remainder / 60);
- wheel.push([ 'minute', tmp, 60 ]);
- remainder -= tmp * 60;
-
- // seconds
- wheel.push([ 'second', Math.floor(remainder), 60 ]);
- return wheel;
- }
-
- function approximate(precision, wheel) {
- // now nudge the wheels into an acceptably (im)precise configuration
- FIX: do {
- // constraints for leaving this block:
- // 1) number of nonzero wheels must be <= precision
- // 2) no wheels can be improperly expressed (like having "60" for mins)
-
- var nonzero_count = 0;
- var improperly_expressed = -1;
-
- for (var i = 0; i < wheel.length; i++) {
- var tmp = wheel[i];
- if (tmp[1] == 0) {
- continue;
- }
- ++nonzero_count;
- if (i == 0) {
- // the years wheel is never improper or over any limit; skip
- continue;
- }
-
- if (nonzero_count > precision) {
- // this is one nonzero wheel too many
-
- // incr previous wheel if we're big enough
- if (tmp[1] >= (tmp[tmp.length - 1] / 2)) {
- ++wheel[i - 1][1];
- }
-
- // reset this and subsequent wheels to 0
- for (var j = i; j < wheel.length; j++) {
- wheel[j][1] = 0;
- }
-
- // start over
- continue FIX;
-
- } else if (tmp[1] >= tmp[tmp.length - 1]) {
- // it's an improperly expressed wheel (like "60" on the mins wheel)
- improperly_expressed = i;
- }
- }
-
- if (improperly_expressed != -1) {
- // only fix the least-significant improperly expressed wheel (at a time)
- ++wheel[improperly_expressed - 1][1];
- wheel[improperly_expressed][1] = 0;
-
- // start over
- continue FIX;
- }
-
- // otherwise there's not too many nonzero wheels, and there's no
- //improperly expressed wheels, so fall thru...
- } while(0);
-
- return wheel;
- }
-
- function render(wheel) {
- var parts = [];
- wheel.forEach(function(element, index) {
- if (element[1] > 0) {
- parts.push(element[1] + ' ' + element[0]);
- if (element[1] != 1) {
- parts[parts.length - 1] += 's';
- }
- }
- });
-
- if (parts.length == 0) {
- return "just now";
- }
- parts[parts.length - 1] += ' ago';
- if (parts.length == 1) {
- return parts[0];
- }
- if (parts.length == 2) {
- return parts[0] + ' and ' + parts[1];
- }
- parts[parts.length - 1] = 'and ' + parts[parts.length - 1];
- return parts.join(', ');
- }
-
- window.setInterval(function() {
- var now = Math.floor(new Date().getTime() / 1000);
- $('.rel-time').each(function() {
- $(this).text(render(approximate(2, separate(now - $(this).data('time')))));
- });
- $('.rel-time-title').each(function() {
- $(this).attr('title', render(approximate(2, separate(now - $(this).data('time')))));
- });
- }, 60000);
-});
diff --git a/extensions/MozReview/web/js/mozreview.js b/extensions/MozReview/web/js/mozreview.js
index 2b9575292..e927d366c 100644
--- a/extensions/MozReview/web/js/mozreview.js
+++ b/extensions/MozReview/web/js/mozreview.js
@@ -8,43 +8,6 @@
var MozReview = {};
-MozReview.formatElapsedTime = function(s, val) {
- val = Math.floor(val);
- return val + ' ' + s + (val == 1 ? '' : 's') + ' ago';
-};
-
-MozReview.elapsedTime = function(d) {
- var ms = Date.now() - d;
-
- var seconds = ms / 1000;
- if (seconds < 60) {
- return MozReview.formatElapsedTime('second', seconds);
- }
-
- var minutes = seconds / 60;
- if (minutes < 60) {
- return MozReview.formatElapsedTime('minute', minutes);
- }
-
- var hours = minutes / 60;
- if (hours < 24) {
- return MozReview.formatElapsedTime('hour', hours);
- }
-
- var days = hours / 24;
- if (days < 30) {
- return MozReview.formatElapsedTime("day", days);
- }
-
- var months = days / 30; // enh fudge it
- if (months < 12) {
- return MozReview.formatElapsedTime("month", months);
- }
-
- var years = months / 12;
- return MozReview.formatElapsedTime("year", years);
-};
-
MozReview.getReviewRequest = function() {
var hostUrl = $('.mozreview-requests').data('mozreviewUrl');
var tr = $('<tr/>');
@@ -83,7 +46,7 @@ MozReview.getReviewRequest = function() {
td.clone().text(rr.status),
td.clone().text(rr.issue_open_count)
.addClass('mozreview-open-issues'),
- td.clone().text(MozReview.elapsedTime(new Date(rr.last_updated)))
+ td.clone().text(timeAgo(new Date(rr.last_updated)))
);
if (rr.status == "discarded") {
diff --git a/extensions/MyDashboard/lib/Queries.pm b/extensions/MyDashboard/lib/Queries.pm
index 34c63e07b..199e8eb46 100644
--- a/extensions/MyDashboard/lib/Queries.pm
+++ b/extensions/MyDashboard/lib/Queries.pm
@@ -15,10 +15,9 @@ use Bugzilla::CGI;
use Bugzilla::Search;
use Bugzilla::Flag;
use Bugzilla::Status qw(is_open_state);
-use Bugzilla::Util qw(format_time datetime_from);
+use Bugzilla::Util qw(format_time datetime_from time_ago);
use Bugzilla::Extension::MyDashboard::Util qw(open_states quoted_open_states);
-use Bugzilla::Extension::MyDashboard::TimeAgo qw(time_ago);
use DateTime;
diff --git a/extensions/MyDashboard/lib/TimeAgo.pm b/extensions/MyDashboard/lib/TimeAgo.pm
deleted file mode 100644
index 0206bfebd..000000000
--- a/extensions/MyDashboard/lib/TimeAgo.pm
+++ /dev/null
@@ -1,179 +0,0 @@
-package Bugzilla::Extension::MyDashboard::TimeAgo;
-
-use strict;
-use utf8;
-use DateTime;
-use Carp;
-use Exporter qw(import);
-
-use if $ENV{ARCH_64BIT}, 'integer';
-
-our @EXPORT_OK = qw(time_ago);
-
-our $VERSION = '0.06';
-
-my @ranges = (
- [ -1, 'in the future' ],
- [ 60, 'just now' ],
- [ 900, 'a few minutes ago'], # 15*60
- [ 3000, 'less than an hour ago'], # 50*60
- [ 4500, 'about an hour ago'], # 75*60
- [ 7200, 'more than an hour ago'], # 2*60*60
- [ 21600, 'several hours ago'], # 6*60*60
- [ 86400, 'today', sub { # 24*60*60
- my $time = shift;
- my $now = shift;
- if ( $time->day < $now->day
- or $time->month < $now->month
- or $time->year < $now->year
- ) {
- return 'yesterday'
- }
- if ($time->hour < 5) {
- return 'tonight'
- }
- if ($time->hour < 10) {
- return 'this morning'
- }
- if ($time->hour < 15) {
- return 'today'
- }
- if ($time->hour < 19) {
- return 'this afternoon'
- }
- return 'this evening'
- }],
- [ 172800, 'yesterday'], # 2*24*60*60
- [ 604800, 'this week'], # 7*24*60*60
- [ 1209600, 'last week'], # 2*7*24*60*60
- [ 2678400, 'this month', sub { # 31*24*60*60
- my $time = shift;
- my $now = shift;
- if ($time->year == $now->year and $time->month == $now->month) {
- return 'this month'
- }
- return 'last month'
- }],
- [ 5356800, 'last month'], # 2*31*24*60*60
- [ 24105600, 'several months ago'], # 9*31*24*60*60
- [ 31536000, 'about a year ago'], # 365*24*60*60
- [ 34214400, 'last year'], # (365+31)*24*60*60
- [ 63072000, 'more than a year ago'], # 2*365*24*60*60
- [ 283824000, 'several years ago'], # 9*365*24*60*60
- [ 315360000, 'about a decade ago'], # 10*365*24*60*60
- [ 630720000, 'last decade'], # 20*365*24*60*60
- [ 2838240000, 'several decades ago'], # 90*365*24*60*60
- [ 3153600000, 'about a century ago'], # 100*365*24*60*60
- [ 6307200000, 'last century'], # 200*365*24*60*60
- [ 6622560000, 'more than a century ago'], # 210*365*24*60*60
- [ 28382400000, 'several centuries ago'], # 900*365*24*60*60
- [ 31536000000, 'about a millenium ago'], # 1000*365*24*60*60
- [ 63072000000, 'more than a millenium ago'], # 2000*365*24*60*60
-);
-
-sub time_ago {
- my ($time, $now) = @_;
-
- if (not defined $time or not $time->isa('DateTime')) {
- croak('DateTime::Duration::Fuzzy::time_ago needs a DateTime object as first parameter')
- }
- if (not defined $now) {
- $now = DateTime->now();
- }
- if (not $now->isa('DateTime')) {
- croak('Invalid second parameter provided to DateTime::Duration::Fuzzy::time_ago; it must be a DateTime object if provided')
- }
-
- my $dur = $now->subtract_datetime_absolute($time)->in_units('seconds');
-
- foreach my $range ( @ranges ) {
- if ( $dur <= $range->[0] ) {
- if ( $range->[2] ) {
- return $range->[2]->($time, $now)
- }
- return $range->[1]
- }
- }
-
- return 'millenia ago'
-}
-
-1
-
-__END__
-
-=head1 NAME
-
-DateTime::Duration::Fuzzy -- express dates as fuzzy human-friendly strings
-
-=head1 SYNOPSIS
-
- use DateTime::Duration::Fuzzy qw(time_ago);
- use DateTime;
-
- my $now = DateTime->new(
- year => 2010, month => 12, day => 12,
- hour => 19, minute => 59,
- );
- my $then = DateTime->new(
- year => 2010, month => 12, day => 12,
- hour => 15,
- );
- print time_ago($then, $now);
- # outputs 'several hours ago'
-
- print time_ago($then);
- # $now taken from C<time> function
-
-=head1 DESCRIPTION
-
-DateTime::Duration::Fuzzy is inspired from the timeAgo jQuery module
-L<http://timeago.yarp.com/>.
-
-It takes two DateTime objects -- first one representing a moment in the past
-and second optional one representine the present, and returns a human-friendly
-fuzzy expression of the time gone.
-
-=head2 functions
-
-=over 4
-
-=item time_ago($then, $now)
-
-The only exportable function.
-
-First obligatory parameter is a DateTime object.
-
-Second optional parameter is also a DateTime object.
-If it's not provided, then I<now> as the C<time> function returns is
-substituted.
-
-Returns a string expression of the interval between the two DateTime
-objects, like C<several hours ago>, C<yesterday> or <last century>.
-
-=back
-
-=head2 performance
-
-On 64bit machines, it is asvisable to 'use integer', which makes
-the calculations faster. You can turn this on by setting the
-C<ARCH_64BIT> environmental variable to a true value.
-
-If you do this on a 32bit machine, you will get wrong results for
-intervals starting with "several decades ago".
-
-=head1 AUTHOR
-
-Jan Oldrich Kruza, C<< <sixtease at cpan.org> >>
-
-=head1 LICENSE AND COPYRIGHT
-
-Copyright 2010 Jan Oldrich Kruza.
-
-This program is free software; you can redistribute it and/or modify it
-under the terms of either: the GNU General Public License as published
-by the Free Software Foundation; or the Artistic License.
-
-See http://dev.perl.org/licenses/ for more information.
-
-=cut
diff --git a/extensions/RequestNagger/Extension.pm b/extensions/RequestNagger/Extension.pm
index 2be828fd1..0ed517244 100644
--- a/extensions/RequestNagger/Extension.pm
+++ b/extensions/RequestNagger/Extension.pm
@@ -14,11 +14,10 @@ use base qw(Bugzilla::Extension);
use Bugzilla::Constants;
use Bugzilla::Error;
-use Bugzilla::Extension::RequestNagger::TimeAgo qw(time_ago);
use Bugzilla::Flag;
use Bugzilla::Install::Filesystem;
use Bugzilla::User::Setting;
-use Bugzilla::Util qw(datetime_from detaint_natural);
+use Bugzilla::Util qw(datetime_from detaint_natural time_ago);
use DateTime;
our $VERSION = '1';
diff --git a/extensions/RequestNagger/lib/TimeAgo.pm b/extensions/RequestNagger/lib/TimeAgo.pm
deleted file mode 100644
index 3dfbbeaac..000000000
--- a/extensions/RequestNagger/lib/TimeAgo.pm
+++ /dev/null
@@ -1,186 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-#
-# This Source Code Form is "Incompatible With Secondary Licenses", as
-# defined by the Mozilla Public License, v. 2.0.
-
-package Bugzilla::Extension::RequestNagger::TimeAgo;
-
-use strict;
-use utf8;
-use DateTime;
-use Carp;
-use Exporter qw(import);
-
-use if $ENV{ARCH_64BIT}, 'integer';
-
-our @EXPORT_OK = qw(time_ago);
-
-our $VERSION = '0.06';
-
-my @ranges = (
- [ -1, 'in the future' ],
- [ 60, 'just now' ],
- [ 900, 'a few minutes ago'], # 15*60
- [ 3000, 'less than an hour ago'], # 50*60
- [ 4500, 'about an hour ago'], # 75*60
- [ 7200, 'more than an hour ago'], # 2*60*60
- [ 21600, 'several hours ago'], # 6*60*60
- [ 86400, 'today', sub { # 24*60*60
- my $time = shift;
- my $now = shift;
- if ( $time->day < $now->day
- or $time->month < $now->month
- or $time->year < $now->year
- ) {
- return 'yesterday'
- }
- if ($time->hour < 5) {
- return 'tonight'
- }
- if ($time->hour < 10) {
- return 'this morning'
- }
- if ($time->hour < 15) {
- return 'today'
- }
- if ($time->hour < 19) {
- return 'this afternoon'
- }
- return 'this evening'
- }],
- [ 172800, 'yesterday'], # 2*24*60*60
- [ 604800, 'this week'], # 7*24*60*60
- [ 1209600, 'last week'], # 2*7*24*60*60
- [ 2678400, 'this month', sub { # 31*24*60*60
- my $time = shift;
- my $now = shift;
- if ($time->year == $now->year and $time->month == $now->month) {
- return 'this month'
- }
- return 'last month'
- }],
- [ 5356800, 'last month'], # 2*31*24*60*60
- [ 24105600, 'several months ago'], # 9*31*24*60*60
- [ 31536000, 'about a year ago'], # 365*24*60*60
- [ 34214400, 'last year'], # (365+31)*24*60*60
- [ 63072000, 'more than a year ago'], # 2*365*24*60*60
- [ 283824000, 'several years ago'], # 9*365*24*60*60
- [ 315360000, 'about a decade ago'], # 10*365*24*60*60
- [ 630720000, 'last decade'], # 20*365*24*60*60
- [ 2838240000, 'several decades ago'], # 90*365*24*60*60
- [ 3153600000, 'about a century ago'], # 100*365*24*60*60
- [ 6307200000, 'last century'], # 200*365*24*60*60
- [ 6622560000, 'more than a century ago'], # 210*365*24*60*60
- [ 28382400000, 'several centuries ago'], # 900*365*24*60*60
- [ 31536000000, 'about a millenium ago'], # 1000*365*24*60*60
- [ 63072000000, 'more than a millenium ago'], # 2000*365*24*60*60
-);
-
-sub time_ago {
- my ($time, $now) = @_;
-
- if (not defined $time or not $time->isa('DateTime')) {
- croak('DateTime::Duration::Fuzzy::time_ago needs a DateTime object as first parameter')
- }
- if (not defined $now) {
- $now = DateTime->now();
- }
- if (not $now->isa('DateTime')) {
- croak('Invalid second parameter provided to DateTime::Duration::Fuzzy::time_ago; it must be a DateTime object if provided')
- }
-
- my $dur = $now->subtract_datetime_absolute($time)->in_units('seconds');
-
- foreach my $range ( @ranges ) {
- if ( $dur <= $range->[0] ) {
- if ( $range->[2] ) {
- return $range->[2]->($time, $now)
- }
- return $range->[1]
- }
- }
-
- return 'millenia ago'
-}
-
-1
-
-__END__
-
-=head1 NAME
-
-DateTime::Duration::Fuzzy -- express dates as fuzzy human-friendly strings
-
-=head1 SYNOPSIS
-
- use DateTime::Duration::Fuzzy qw(time_ago);
- use DateTime;
-
- my $now = DateTime->new(
- year => 2010, month => 12, day => 12,
- hour => 19, minute => 59,
- );
- my $then = DateTime->new(
- year => 2010, month => 12, day => 12,
- hour => 15,
- );
- print time_ago($then, $now);
- # outputs 'several hours ago'
-
- print time_ago($then);
- # $now taken from C<time> function
-
-=head1 DESCRIPTION
-
-DateTime::Duration::Fuzzy is inspired from the timeAgo jQuery module
-L<http://timeago.yarp.com/>.
-
-It takes two DateTime objects -- first one representing a moment in the past
-and second optional one representine the present, and returns a human-friendly
-fuzzy expression of the time gone.
-
-=head2 functions
-
-=over 4
-
-=item time_ago($then, $now)
-
-The only exportable function.
-
-First obligatory parameter is a DateTime object.
-
-Second optional parameter is also a DateTime object.
-If it's not provided, then I<now> as the C<time> function returns is
-substituted.
-
-Returns a string expression of the interval between the two DateTime
-objects, like C<several hours ago>, C<yesterday> or <last century>.
-
-=back
-
-=head2 performance
-
-On 64bit machines, it is asvisable to 'use integer', which makes
-the calculations faster. You can turn this on by setting the
-C<ARCH_64BIT> environmental variable to a true value.
-
-If you do this on a 32bit machine, you will get wrong results for
-intervals starting with "several decades ago".
-
-=head1 AUTHOR
-
-Jan Oldrich Kruza, C<< <sixtease at cpan.org> >>
-
-=head1 LICENSE AND COPYRIGHT
-
-Copyright 2010 Jan Oldrich Kruza.
-
-This program is free software; you can redistribute it and/or modify it
-under the terms of either: the GNU General Public License as published
-by the Free Software Foundation; or the Artistic License.
-
-See http://dev.perl.org/licenses/ for more information.
-
-=cut
diff --git a/extensions/UserProfile/Extension.pm b/extensions/UserProfile/Extension.pm
index 8671ba755..4fe1436c0 100644
--- a/extensions/UserProfile/Extension.pm
+++ b/extensions/UserProfile/Extension.pm
@@ -13,11 +13,10 @@ use warnings;
use base qw(Bugzilla::Extension);
use Bugzilla::Constants;
-use Bugzilla::Extension::UserProfile::TimeAgo qw(time_ago);
use Bugzilla::Extension::UserProfile::Util;
use Bugzilla::Install::Filesystem;
use Bugzilla::User;
-use Bugzilla::Util qw(datetime_from);
+use Bugzilla::Util qw(datetime_from time_ago);
use Email::Address;
use Scalar::Util qw(blessed);
diff --git a/extensions/UserProfile/lib/TimeAgo.pm b/extensions/UserProfile/lib/TimeAgo.pm
deleted file mode 100644
index d20f0edf5..000000000
--- a/extensions/UserProfile/lib/TimeAgo.pm
+++ /dev/null
@@ -1,179 +0,0 @@
-package Bugzilla::Extension::UserProfile::TimeAgo;
-
-use strict;
-use utf8;
-use DateTime;
-use Carp;
-use Exporter qw(import);
-
-use if $ENV{ARCH_64BIT}, 'integer';
-
-our @EXPORT_OK = qw(time_ago);
-
-our $VERSION = '0.06';
-
-my @ranges = (
- [ -1, 'in the future' ],
- [ 60, 'just now' ],
- [ 900, 'a few minutes ago'], # 15*60
- [ 3000, 'less than an hour ago'], # 50*60
- [ 4500, 'about an hour ago'], # 75*60
- [ 7200, 'more than an hour ago'], # 2*60*60
- [ 21600, 'several hours ago'], # 6*60*60
- [ 86400, 'today', sub { # 24*60*60
- my $time = shift;
- my $now = shift;
- if ( $time->day < $now->day
- or $time->month < $now->month
- or $time->year < $now->year
- ) {
- return 'yesterday'
- }
- if ($time->hour < 5) {
- return 'tonight'
- }
- if ($time->hour < 10) {
- return 'this morning'
- }
- if ($time->hour < 15) {
- return 'today'
- }
- if ($time->hour < 19) {
- return 'this afternoon'
- }
- return 'this evening'
- }],
- [ 172800, 'yesterday'], # 2*24*60*60
- [ 604800, 'this week'], # 7*24*60*60
- [ 1209600, 'last week'], # 2*7*24*60*60
- [ 2678400, 'this month', sub { # 31*24*60*60
- my $time = shift;
- my $now = shift;
- if ($time->year == $now->year and $time->month == $now->month) {
- return 'this month'
- }
- return 'last month'
- }],
- [ 5356800, 'last month'], # 2*31*24*60*60
- [ 24105600, 'several months ago'], # 9*31*24*60*60
- [ 31536000, 'about a year ago'], # 365*24*60*60
- [ 34214400, 'last year'], # (365+31)*24*60*60
- [ 63072000, 'more than a year ago'], # 2*365*24*60*60
- [ 283824000, 'several years ago'], # 9*365*24*60*60
- [ 315360000, 'about a decade ago'], # 10*365*24*60*60
- [ 630720000, 'last decade'], # 20*365*24*60*60
- [ 2838240000, 'several decades ago'], # 90*365*24*60*60
- [ 3153600000, 'about a century ago'], # 100*365*24*60*60
- [ 6307200000, 'last century'], # 200*365*24*60*60
- [ 6622560000, 'more than a century ago'], # 210*365*24*60*60
- [ 28382400000, 'several centuries ago'], # 900*365*24*60*60
- [ 31536000000, 'about a millenium ago'], # 1000*365*24*60*60
- [ 63072000000, 'more than a millenium ago'], # 2000*365*24*60*60
-);
-
-sub time_ago {
- my ($time, $now) = @_;
-
- if (not defined $time or not $time->isa('DateTime')) {
- croak('DateTime::Duration::Fuzzy::time_ago needs a DateTime object as first parameter')
- }
- if (not defined $now) {
- $now = DateTime->now();
- }
- if (not $now->isa('DateTime')) {
- croak('Invalid second parameter provided to DateTime::Duration::Fuzzy::time_ago; it must be a DateTime object if provided')
- }
-
- my $dur = $now->subtract_datetime_absolute($time)->in_units('seconds');
-
- foreach my $range ( @ranges ) {
- if ( $dur <= $range->[0] ) {
- if ( $range->[2] ) {
- return $range->[2]->($time, $now)
- }
- return $range->[1]
- }
- }
-
- return 'millenia ago'
-}
-
-1
-
-__END__
-
-=head1 NAME
-
-DateTime::Duration::Fuzzy -- express dates as fuzzy human-friendly strings
-
-=head1 SYNOPSIS
-
- use DateTime::Duration::Fuzzy qw(time_ago);
- use DateTime;
-
- my $now = DateTime->new(
- year => 2010, month => 12, day => 12,
- hour => 19, minute => 59,
- );
- my $then = DateTime->new(
- year => 2010, month => 12, day => 12,
- hour => 15,
- );
- print time_ago($then, $now);
- # outputs 'several hours ago'
-
- print time_ago($then);
- # $now taken from C<time> function
-
-=head1 DESCRIPTION
-
-DateTime::Duration::Fuzzy is inspired from the timeAgo jQuery module
-L<http://timeago.yarp.com/>.
-
-It takes two DateTime objects -- first one representing a moment in the past
-and second optional one representine the present, and returns a human-friendly
-fuzzy expression of the time gone.
-
-=head2 functions
-
-=over 4
-
-=item time_ago($then, $now)
-
-The only exportable function.
-
-First obligatory parameter is a DateTime object.
-
-Second optional parameter is also a DateTime object.
-If it's not provided, then I<now> as the C<time> function returns is
-substituted.
-
-Returns a string expression of the interval between the two DateTime
-objects, like C<several hours ago>, C<yesterday> or <last century>.
-
-=back
-
-=head2 performance
-
-On 64bit machines, it is asvisable to 'use integer', which makes
-the calculations faster. You can turn this on by setting the
-C<ARCH_64BIT> environmental variable to a true value.
-
-If you do this on a 32bit machine, you will get wrong results for
-intervals starting with "several decades ago".
-
-=head1 AUTHOR
-
-Jan Oldrich Kruza, C<< <sixtease at cpan.org> >>
-
-=head1 LICENSE AND COPYRIGHT
-
-Copyright 2010 Jan Oldrich Kruza.
-
-This program is free software; you can redistribute it and/or modify it
-under the terms of either: the GNU General Public License as published
-by the Free Software Foundation; or the Artistic License.
-
-See http://dev.perl.org/licenses/ for more information.
-
-=cut
diff --git a/js/util.js b/js/util.js
index 78b64b516..c27dc2b11 100644
--- a/js/util.js
+++ b/js/util.js
@@ -341,3 +341,30 @@ function bz_toggleClass(anElement, aClass) {
YAHOO.util.Dom.addClass(anElement, aClass);
}
}
+
+/* Retruns a string representation of a duration.
+ *
+ * @param ss Duration in seconds
+ * or
+ * @param date Date object
+ */
+function timeAgo(param) {
+ var ss = param.constructor === Date ? Math.round((new Date() - param) / 1000) : param;
+ var mm = Math.round(ss / 60),
+ hh = Math.round(mm / 60),
+ dd = Math.round(hh / 24),
+ mo = Math.round(dd / 30),
+ yy = Math.round(mo / 12);
+ if (ss < 10) return 'just now';
+ if (ss < 45) return ss + ' seconds ago';
+ if (ss < 90) return 'a minute ago';
+ if (mm < 45) return mm + ' minutes ago';
+ if (mm < 90) return 'an hour ago';
+ if (hh < 24) return hh + ' hours ago';
+ if (hh < 36) return 'a day ago';
+ if (dd < 30) return dd + ' days ago';
+ if (dd < 45) return 'a month ago';
+ if (mo < 12) return mo + ' months ago';
+ if (mo < 18) return 'a year ago';
+ return yy + ' years ago';
+}