Fixing TimeHelper::timeAgoInWords()/relativeTime(), fixes #4241

git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@6485 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
nate 2008-02-28 05:06:37 +00:00
parent c8ed9424da
commit b5d0baf98b
2 changed files with 227 additions and 44 deletions

View file

@ -260,18 +260,15 @@ class TimeHelper extends AppHelper {
*
* @param string $date_string Datetime string or Unix timestamp
* @param array $options Default format if timestamp is used in $date_string
* @param string $backwards False if $date_string is in the past, true if in the future
* @return string Relative time string.
*/
function timeAgoInWords($dateTime, $options = array(), $backwards = null) {
function timeAgoInWords($dateTime, $options = array()) {
$in_seconds = $this->fromString($dateTime);
if ($backwards === null && $in_seconds > time()) {
$backwards = true;
}
$backwards = ($in_seconds > time());
$format = 'j/n/y';
$end = '+1 month';//when to show format
$end = '+1 month';
$now = time();
if (is_array($options)) {
if (isset($options['format'])) {
@ -287,29 +284,92 @@ class TimeHelper extends AppHelper {
}
if ($backwards) {
$start = floor(abs($in_seconds - time()));
$future_time = $in_seconds;
$past_time = $now;
} else {
$start = floor(abs(time() - $in_seconds));
$future_time = $now;
$past_time = $in_seconds;
}
$diff = $future_time - $past_time;
$months = floor($start / 2638523.0769231);
$diff = $start - $months * 2638523.0769231;
$weeks = floor($diff / 604800);
$diff -= $weeks * 604800;
$days = floor($diff / 86400);
$diff -= $days * 86400;
$hours = floor($diff / 3600);
$diff -= $hours * 3600;
$minutes = floor($diff / 60);
$diff -= $minutes * 60;
$seconds = $diff;
// If more than a week, then take into account the length of months
if($diff >= 604800) {
$current = array();
$date = array();
list($future['H'], $future['i'], $future['s'], $future['d'], $future['m'], $future['Y']) = explode('/', date('H/i/s/d/m/Y', $future_time));
list($past['H'], $past['i'], $past['s'], $past['d'], $past['m'], $past['Y']) = explode('/', date('H/i/s/d/m/Y', $past_time));
$years = $months = $weeks = $days = $hours = $minutes = $seconds = 0;
if($future['Y'] == $past['Y'] && $future['m'] == $past['m']) {
$months = 0;
$years = 0;
} else {
if($future['Y'] == $past['Y']) {
$months = $future['m'] - $past['m'];
} else {
$years = $future['Y'] - $past['Y'];
$months = $future['m'] + ((12 * $years) - $past['m']);
if($months >= 12) {
$years = floor($months / 12);
$months = $months - ($years * 12);
}
if($future['m'] < $past['m'] && $future['Y'] - $past['Y'] == 1) {
$years --;
}
}
}
if($future['d'] >= $past['d']) {
$days = $future['d'] - $past['d'];
} else {
$days_in_past_month = date('t', $past_time);
$days_in_future_month = date('t', mktime(0, 0, 0, $future['m'] - 1, 1, $future['Y']));
$days = ($days_in_future_month - $past['d']) + $future['d'];
if($future['m'] != $past['m']) {
$months --;
}
}
if($months == 0 && $years >= 1 && $diff < ($years * 31536000)){
$months = 11;
$years --;
}
if($months >= 12) {
$years = $years + 1;
$months = $months - 12;
}
if($days >= 7) {
$weeks = floor($days / 7);
$days = $days - ($weeks * 7);
}
} else {
$years = $months = $weeks = 0;
$days = floor($diff / 86400);
$diff = $diff - ($days * 86400);
$hours = floor($diff / 3600);
$diff = $diff - ($hours * 3600);
$minutes = floor($diff / 60);
$diff = $diff - ($minutes * 60);
$seconds = $diff;
}
$relative_date = '';
$diff = $future_time - $past_time;
if ($start > abs(time() - $this->fromString($end))) {
if ($diff > abs($now - $this->fromString($end))) {
$relative_date = 'on ' . date($format, $in_seconds);
} else {
if (abs($months) > 0) {
if ($years > 0) {
// years and months and days
$relative_date .= ($relative_date ? ', ' : '') . $years . ' year' . ($years > 1 ? 's' : '');
$relative_date .= $months > 0 ? ($relative_date ? ', ' : '') . $months . ' month' . ($months > 1 ? 's' : '') : '';
$relative_date .= $weeks > 0 ? ($relative_date ? ', ' : '') . $weeks . ' week' . ($weeks > 1 ? 's' : '') : '';
$relative_date .= $days > 0 ? ($relative_date ? ', ' : '') . $days . ' day' . ($days > 1 ? 's' : '') : '';
} elseif (abs($months) > 0) {
// months, weeks and days
$relative_date .= ($relative_date ? ', ' : '') . $months . ' month' . ($months > 1 ? 's' : '');
$relative_date .= $weeks > 0 ? ($relative_date ? ', ' : '') . $weeks . ' week' . ($weeks > 1 ? 's' : '') : '';
@ -341,7 +401,7 @@ class TimeHelper extends AppHelper {
return $this->output($relative_date);
}
/**
* Alias for timeAgoInWords, but can also calculate dates in the future
* Alias for timeAgoInWords
*
* @param mixed $dateTime Datetime string (strtotime-compatible) or Unix timestamp
* @param mixed $options Default format string, if timestamp is used in $dateTime, or an array of options to be passed
@ -349,9 +409,8 @@ class TimeHelper extends AppHelper {
* @return string Relative time string.
* @see TimeHelper::timeAgoInWords
*/
function relativeTime($dateTime, $format = 'j/n/y') {
$date = $this->fromString($dateTime);
return $this->timeAgoInWords($dateTime, $format, (strtotime("now") <= $date));
function relativeTime($dateTime, $options = array()) {
return $this->timeAgoInWords($dateTime, $options);
}
/**
* Returns true if specified datetime was within the interval specified, else false.

View file

@ -59,6 +59,142 @@ class TimeTest extends UnitTestCase {
}
function testTimeAgoInWords() {
$result = $this->Time->timeAgoInWords(strtotime('4 months, 2 weeks, 3 days'), array('end' => '8 years'), true);
$this->assertEqual($result, '4 months, 2 weeks, 3 days');
$result = $this->Time->timeAgoInWords(strtotime('4 months, 2 weeks, 2 days'), array('end' => '8 years'), true);
$this->assertEqual($result, '4 months, 2 weeks, 2 days');
$result = $this->Time->timeAgoInWords(strtotime('4 months, 2 weeks, 1 day'), array('end' => '8 years'), true);
$this->assertEqual($result, '4 months, 2 weeks, 1 day');
$result = $this->Time->timeAgoInWords(strtotime('3 months, 2 weeks, 1 day'), array('end' => '8 years'), true);
$this->assertEqual($result, '3 months, 2 weeks, 1 day');
$result = $this->Time->timeAgoInWords(strtotime('3 months, 2 weeks'), array('end' => '8 years'), true);
$this->assertEqual($result, '3 months, 2 weeks');
$result = $this->Time->timeAgoInWords(strtotime('3 months, 1 week, 6 days'), array('end' => '8 years'), true);
$this->assertEqual($result, '3 months, 1 week, 6 days');
$result = $this->Time->timeAgoInWords(strtotime('2 months, 2 weeks, 1 day'), array('end' => '8 years'), true);
$this->assertEqual($result, '2 months, 2 weeks, 1 day');
$result = $this->Time->timeAgoInWords(strtotime('2 months, 2 weeks'), array('end' => '8 years'), true);
$this->assertEqual($result, '2 months, 2 weeks');
$result = $this->Time->timeAgoInWords(strtotime('2 months, 1 week, 6 days'), array('end' => '8 years'), true);
$this->assertEqual($result, '2 months, 1 week, 6 days');
$result = $this->Time->timeAgoInWords(strtotime('1 month, 1 week, 6 days'), array('end' => '8 years'), true);
$this->assertEqual($result, '1 month, 1 week, 6 days');
for($i = 0; $i < 200; $i ++) {
$years = rand(0, 3);
$months = rand(0, 11);
$weeks = rand(0, 3);
$days = rand(0, 6);
$hours = 0;
$minutes = 0;
$seconds = 0;
$relative_date = '';
if($years > 0) {
// years and months and days
$relative_date .= ($relative_date ? ', -' : '-') . $years . ' year' . ($years > 1 ? 's' : '');
$relative_date .= $months > 0 ? ($relative_date ? ', -' : '-') . $months . ' month' . ($months > 1 ? 's' : '') : '';
$relative_date .= $weeks > 0 ? ($relative_date ? ', -' : '-') . $weeks . ' week' . ($weeks > 1 ? 's' : '') : '';
$relative_date .= $days > 0 ? ($relative_date ? ', -' : '-') . $days . ' day' . ($days > 1 ? 's' : '') : '';
} elseif (abs($months) > 0) {
// months, weeks and days
$relative_date .= ($relative_date ? ', -' : '-') . $months . ' month' . ($months > 1 ? 's' : '');
$relative_date .= $weeks > 0 ? ($relative_date ? ', -' : '-') . $weeks . ' week' . ($weeks > 1 ? 's' : '') : '';
$relative_date .= $days > 0 ? ($relative_date ? ', -' : '-') . $days . ' day' . ($days > 1 ? 's' : '') : '';
} elseif (abs($weeks) > 0) {
// weeks and days
$relative_date .= ($relative_date ? ', -' : '-') . $weeks . ' week' . ($weeks > 1 ? 's' : '');
$relative_date .= $days > 0 ? ($relative_date ? ', -' : '-') . $days . ' day' . ($days > 1 ? 's' : '') : '';
} elseif (abs($days) > 0) {
// days and hours
$relative_date .= ($relative_date ? ', -' : '-') . $days . ' day' . ($days > 1 ? 's' : '');
$relative_date .= $hours > 0 ? ($relative_date ? ', -' : '-') . $hours . ' hour' . ($hours > 1 ? 's' : '') : '';
} elseif (abs($hours) > 0) {
// hours and minutes
$relative_date .= ($relative_date ? ', -' : '-') . $hours . ' hour' . ($hours > 1 ? 's' : '');
$relative_date .= $minutes > 0 ? ($relative_date ? ', -' : '-') . $minutes . ' minute' . ($minutes > 1 ? 's' : '') : '';
} elseif (abs($minutes) > 0) {
// minutes only
$relative_date .= ($relative_date ? ', -' : '-') . $minutes . ' minute' . ($minutes > 1 ? 's' : '');
} else {
// seconds only
$relative_date .= ($relative_date ? ', -' : '-') . $seconds . ' second' . ($seconds != 1 ? 's' : '');
}
if(date('j/n/y', strtotime($relative_date)) != '1/1/70') {
$result = $this->Time->timeAgoInWords(strtotime($relative_date), array('end' => '8 years'), true);
if($relative_date == '0 seconds') {
$relative_date = '0 seconds ago';
}
$relative_date = str_replace('-', '', $relative_date) . ' ago';
$this->assertEqual($result, $relative_date);
}
}
for($i = 0; $i < 200; $i ++) {
$years = rand(0, 3);
$months = rand(0, 11);
$weeks = rand(0, 3);
$days = rand(0, 6);
$hours = 0;
$minutes = 0;
$seconds = 0;
$relative_date = '';
if($years > 0) {
// years and months and days
$relative_date .= ($relative_date ? ', ' : '') . $years . ' year' . ($years > 1 ? 's' : '');
$relative_date .= $months > 0 ? ($relative_date ? ', ' : '') . $months . ' month' . ($months > 1 ? 's' : '') : '';
$relative_date .= $weeks > 0 ? ($relative_date ? ', ' : '') . $weeks . ' week' . ($weeks > 1 ? 's' : '') : '';
$relative_date .= $days > 0 ? ($relative_date ? ', ' : '') . $days . ' day' . ($days > 1 ? 's' : '') : '';
} elseif (abs($months) > 0) {
// months, weeks and days
$relative_date .= ($relative_date ? ', ' : '') . $months . ' month' . ($months > 1 ? 's' : '');
$relative_date .= $weeks > 0 ? ($relative_date ? ', ' : '') . $weeks . ' week' . ($weeks > 1 ? 's' : '') : '';
$relative_date .= $days > 0 ? ($relative_date ? ', ' : '') . $days . ' day' . ($days > 1 ? 's' : '') : '';
} elseif (abs($weeks) > 0) {
// weeks and days
$relative_date .= ($relative_date ? ', ' : '') . $weeks . ' week' . ($weeks > 1 ? 's' : '');
$relative_date .= $days > 0 ? ($relative_date ? ', ' : '') . $days . ' day' . ($days > 1 ? 's' : '') : '';
} elseif (abs($days) > 0) {
// days and hours
$relative_date .= ($relative_date ? ', ' : '') . $days . ' day' . ($days > 1 ? 's' : '');
$relative_date .= $hours > 0 ? ($relative_date ? ', ' : '') . $hours . ' hour' . ($hours > 1 ? 's' : '') : '';
} elseif (abs($hours) > 0) {
// hours and minutes
$relative_date .= ($relative_date ? ', ' : '') . $hours . ' hour' . ($hours > 1 ? 's' : '');
$relative_date .= $minutes > 0 ? ($relative_date ? ', ' : '') . $minutes . ' minute' . ($minutes > 1 ? 's' : '') : '';
} elseif (abs($minutes) > 0) {
// minutes only
$relative_date .= ($relative_date ? ', ' : '') . $minutes . ' minute' . ($minutes > 1 ? 's' : '');
} else {
// seconds only
$relative_date .= ($relative_date ? ', ' : '') . $seconds . ' second' . ($seconds != 1 ? 's' : '');
}
if(date('j/n/y', strtotime($relative_date)) != '1/1/70') {
$result = $this->Time->timeAgoInWords(strtotime($relative_date), array('end' => '8 years'), true);
if($relative_date == '0 seconds') {
$relative_date = '0 seconds ago';
}
$relative_date = str_replace('-', '', $relative_date) . '';
$this->assertEqual($result, $relative_date);
}
}
$result = $this->Time->timeAgoInWords(strtotime('-2 years, -5 months, -2 days'), array('end' => '3 years'), true);
$this->assertEqual($result, '2 years, 5 months, 2 days ago');
$result = $this->Time->timeAgoInWords('2007-9-25');
$this->assertEqual($result, 'on 25/9/07');
@ -83,26 +219,14 @@ class TimeTest extends UnitTestCase {
$result = $this->Time->timeAgoInWords(strtotime('2 months, 12 days'), array('end' => '3 month'));
$this->assertPattern('/2 months, 1 week/', $result);
if (date('n') == '2') {
$result = $this->Time->timeAgoInWords(strtotime('3 months, 5 days'), array('end' => '4 month'));
$this->assertEqual($result, '3 months, 3 days');
$result = $this->Time->timeAgoInWords(strtotime('3 months, 5 days'), array('end' => '4 month'));
$this->assertEqual($result, '3 months, 5 days');
$result = $this->Time->timeAgoInWords(strtotime('-2 months, -2 days'), array('end' => '3 month'));
$this->assertEqual($result, '2 months, 2 days ago');
$result = $this->Time->timeAgoInWords(strtotime('-2 months, -2 days'), array('end' => '3 month'));
$this->assertEqual($result, '2 months, 2 days ago');
$result = $this->Time->timeAgoInWords(strtotime('-2 months, -2 days'), array('end' => '3 month'));
$this->assertEqual($result, '2 months, 2 days ago');
} else {
// These tests fail in the month of February
$result = $this->Time->timeAgoInWords(strtotime('3 months, 5 days'), array('end' => '4 month'));
$this->assertEqual($result, '3 months, 4 days');
$result = $this->Time->timeAgoInWords(strtotime('-2 months, -2 days'), array('end' => '3 month'));
$this->assertEqual($result, '2 months, 1 day ago');
$result = $this->Time->timeAgoInWords(strtotime('-2 months, -2 days'), array('end' => '3 month'));
$this->assertEqual($result, '2 months, 1 day ago');
}
$result = $this->Time->timeAgoInWords(strtotime('-2 months, -2 days'), array('end' => '3 month'));
$this->assertEqual($result, '2 months, 2 days ago');
$result = $this->Time->timeAgoInWords(strtotime('2 months, 2 days'), array('end' => '3 month'));
$this->assertPattern('/2 months/', $result);