cleans up CakeTime::timeAgoInWords and adds option for custom futures string

This commit is contained in:
Schlaefer 2014-10-01 10:35:41 +02:00
parent af43bc1706
commit b96eb1fb46
2 changed files with 114 additions and 104 deletions

View file

@ -108,9 +108,9 @@ class CakeTimeTest extends CakeTestCase {
array('-2 days -3 hours', '2 days, 3 hours ago'), array('-2 days -3 hours', '2 days, 3 hours ago'),
array('-1 week', '1 week ago'), array('-1 week', '1 week ago'),
array('-2 weeks -2 days', '2 weeks, 2 days ago'), array('-2 weeks -2 days', '2 weeks, 2 days ago'),
array('+1 week', '1 week'), array('+1 week', 'in 1 week'),
array('+1 week 1 day', '1 week, 1 day'), array('+1 week 1 day', 'in 1 week, 1 day'),
array('+2 weeks 2 day', '2 weeks, 2 days'), array('+2 weeks 2 day', 'in 2 weeks, 2 days'),
array('2007-9-24', 'on 24/9/07'), array('2007-9-24', 'on 24/9/07'),
array('now', 'just now'), array('now', 'just now'),
); );
@ -136,37 +136,37 @@ class CakeTimeTest extends CakeTestCase {
return array( return array(
array( array(
'+4 months +2 weeks +3 days', '+4 months +2 weeks +3 days',
'4 months, 2 weeks, 3 days', 'in 4 months, 2 weeks, 3 days',
'8 years' '8 years'
), ),
array( array(
'+4 months +2 weeks +1 day', '+4 months +2 weeks +1 day',
'4 months, 2 weeks, 1 day', 'in 4 months, 2 weeks, 1 day',
'8 years' '8 years'
), ),
array( array(
'+3 months +2 weeks', '+3 months +2 weeks',
'3 months, 2 weeks', 'in 3 months, 2 weeks',
'8 years' '8 years'
), ),
array( array(
'+3 months +2 weeks +1 day', '+3 months +2 weeks +1 day',
'3 months, 2 weeks, 1 day', 'in 3 months, 2 weeks, 1 day',
'8 years' '8 years'
), ),
array( array(
'+1 months +1 week +1 day', '+1 months +1 week +1 day',
'1 month, 1 week, 1 day', 'in 1 month, 1 week, 1 day',
'8 years' '8 years'
), ),
array( array(
'+2 months +2 days', '+2 months +2 days',
'2 months, 2 days', 'in 2 months, 2 days',
'on ' . date('j/n/y', strtotime('+2 months +2 days')) 'on ' . date('j/n/y', strtotime('+2 months +2 days'))
), ),
array( array(
'+2 months +12 days', '+2 months +12 days',
'2 months, 1 week, 5 days', 'in 2 months, 1 week, 5 days',
'3 months' '3 months'
), ),
); );
@ -198,6 +198,13 @@ class CakeTimeTest extends CakeTestCase {
$expected = 'at least 8 years ago'; $expected = 'at least 8 years ago';
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = $this->Time->timeAgoInWords(
strtotime('+8 years +4 months +2 weeks +3 days'),
array('relativeStringFuture' => 'not in the next %s', 'accuracy' => array('year' => 'year'), 'end' => '+10 years')
);
$expected = 'not in the next 8 years';
$this->assertEquals($expected, $result);
$result = $this->Time->timeAgoInWords( $result = $this->Time->timeAgoInWords(
strtotime('+4 months +2 weeks +3 days'), strtotime('+4 months +2 weeks +3 days'),
array('absoluteString' => 'exactly on %s', 'accuracy' => array('year' => 'year'), 'end' => '+2 months') array('absoluteString' => 'exactly on %s', 'accuracy' => array('year' => 'year'), 'end' => '+2 months')
@ -216,35 +223,35 @@ class CakeTimeTest extends CakeTestCase {
strtotime('+8 years +4 months +2 weeks +3 days'), strtotime('+8 years +4 months +2 weeks +3 days'),
array('accuracy' => array('year' => 'year'), 'end' => '+10 years') array('accuracy' => array('year' => 'year'), 'end' => '+10 years')
); );
$expected = '8 years'; $expected = 'in 8 years';
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = $this->Time->timeAgoInWords( $result = $this->Time->timeAgoInWords(
strtotime('+8 years +4 months +2 weeks +3 days'), strtotime('+8 years +4 months +2 weeks +3 days'),
array('accuracy' => array('year' => 'month'), 'end' => '+10 years') array('accuracy' => array('year' => 'month'), 'end' => '+10 years')
); );
$expected = '8 years, 4 months'; $expected = 'in 8 years, 4 months';
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = $this->Time->timeAgoInWords( $result = $this->Time->timeAgoInWords(
strtotime('+8 years +4 months +2 weeks +3 days'), strtotime('+8 years +4 months +2 weeks +3 days'),
array('accuracy' => array('year' => 'week'), 'end' => '+10 years') array('accuracy' => array('year' => 'week'), 'end' => '+10 years')
); );
$expected = '8 years, 4 months, 2 weeks'; $expected = 'in 8 years, 4 months, 2 weeks';
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = $this->Time->timeAgoInWords( $result = $this->Time->timeAgoInWords(
strtotime('+8 years +4 months +2 weeks +3 days'), strtotime('+8 years +4 months +2 weeks +3 days'),
array('accuracy' => array('year' => 'day'), 'end' => '+10 years') array('accuracy' => array('year' => 'day'), 'end' => '+10 years')
); );
$expected = '8 years, 4 months, 2 weeks, 3 days'; $expected = 'in 8 years, 4 months, 2 weeks, 3 days';
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = $this->Time->timeAgoInWords( $result = $this->Time->timeAgoInWords(
strtotime('+1 years +5 weeks'), strtotime('+1 years +5 weeks'),
array('accuracy' => array('year' => 'year'), 'end' => '+10 years') array('accuracy' => array('year' => 'year'), 'end' => '+10 years')
); );
$expected = '1 year'; $expected = 'in 1 year';
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = $this->Time->timeAgoInWords( $result = $this->Time->timeAgoInWords(
@ -278,13 +285,13 @@ class CakeTimeTest extends CakeTestCase {
strtotime('+2 weeks +2 days'), strtotime('+2 weeks +2 days'),
'Y-m-d' 'Y-m-d'
); );
$this->assertRegExp('/^2 weeks, [1|2] day(s)?$/', $result); $this->assertRegExp('/^in 2 weeks, [1|2] day(s)?$/', $result);
$result = $this->Time->timeAgoInWords( $result = $this->Time->timeAgoInWords(
strtotime('+2 weeks +2 days'), strtotime('+2 weeks +2 days'),
'%x' '%x'
); );
$this->assertRegExp('/^2 weeks, [1|2] day(s)?$/', $result); $this->assertRegExp('/^in 2 weeks, [1|2] day(s)?$/', $result);
$result = $this->Time->timeAgoInWords( $result = $this->Time->timeAgoInWords(
strtotime('+2 months +2 days'), strtotime('+2 months +2 days'),

View file

@ -65,13 +65,13 @@ class CakeTime {
* @see CakeTime::timeAgoInWords() * @see CakeTime::timeAgoInWords()
*/ */
public static $wordAccuracy = array( public static $wordAccuracy = array(
'year' => "day", 'year' => 'day',
'month' => "day", 'month' => 'day',
'week' => "day", 'week' => 'day',
'day' => "hour", 'day' => 'hour',
'hour' => "minute", 'hour' => 'minute',
'minute' => "minute", 'minute' => 'minute',
'second' => "second", 'second' => 'second',
); );
/** /**
@ -713,9 +713,10 @@ class CakeTime {
* - minute => The format if minutes > 0 (default "minute") * - minute => The format if minutes > 0 (default "minute")
* - second => The format if seconds > 0 (default "second") * - second => The format if seconds > 0 (default "second")
* - `end` => The end of relative time telling * - `end` => The end of relative time telling
* - `relativeString` => The printf compatible string when outputting relative time * - `relativeString` => The printf compatible string when outputting past relative time
* - `relativeStringFuture` => The printf compatible string when outputting future relative time
* - `absoluteString` => The printf compatible string when outputting absolute time * - `absoluteString` => The printf compatible string when outputting absolute time
* - `userOffset` => Users offset from GMT (in hours) *Deprecated* use timezone intead. * - `userOffset` => Users offset from GMT (in hours) *Deprecated* use timezone instead.
* - `timezone` => The user timezone the timestamp should be formatted in. * - `timezone` => The user timezone the timestamp should be formatted in.
* *
* Relative dates look something like this: * Relative dates look something like this:
@ -737,13 +738,16 @@ class CakeTime {
*/ */
public static function timeAgoInWords($dateTime, $options = array()) { public static function timeAgoInWords($dateTime, $options = array()) {
$timezone = null; $timezone = null;
$accuracies = self::$wordAccuracy;
$format = self::$wordFormat; $format = self::$wordFormat;
$end = self::$wordEnd; $relativeEnd = self::$wordEnd;
$relativeString = __d('cake', '%s ago'); $relativeStringPast = __d('cake', '%s ago');
$relativeStringFuture = __d('cake', 'in %s');
$absoluteString = __d('cake', 'on %s'); $absoluteString = __d('cake', 'on %s');
$accuracy = self::$wordAccuracy;
if (is_array($options)) { if (is_string($options)) {
$format = $options;
} elseif (!empty($options)) {
if (isset($options['timezone'])) { if (isset($options['timezone'])) {
$timezone = $options['timezone']; $timezone = $options['timezone'];
} elseif (isset($options['userOffset'])) { } elseif (isset($options['userOffset'])) {
@ -752,10 +756,10 @@ class CakeTime {
if (isset($options['accuracy'])) { if (isset($options['accuracy'])) {
if (is_array($options['accuracy'])) { if (is_array($options['accuracy'])) {
$accuracy = array_merge($accuracy, $options['accuracy']); $accuracies = array_merge($accuracies, $options['accuracy']);
} else { } else {
foreach ($accuracy as $key => $level) { foreach ($accuracies as $key => $level) {
$accuracy[$key] = $options['accuracy']; $accuracies[$key] = $options['accuracy'];
} }
} }
} }
@ -764,51 +768,56 @@ class CakeTime {
$format = $options['format']; $format = $options['format'];
} }
if (isset($options['end'])) { if (isset($options['end'])) {
$end = $options['end']; $relativeEnd = $options['end'];
} }
if (isset($options['relativeString'])) { if (isset($options['relativeString'])) {
$relativeString = $options['relativeString']; $relativeStringPast = $options['relativeString'];
unset($options['relativeString']); unset($options['relativeString']);
} }
if (isset($options['relativeStringFuture'])) {
$relativeStringFuture = $options['relativeStringFuture'];
unset($options['relativeStringFuture']);
}
if (isset($options['absoluteString'])) { if (isset($options['absoluteString'])) {
$absoluteString = $options['absoluteString']; $absoluteString = $options['absoluteString'];
unset($options['absoluteString']); unset($options['absoluteString']);
} }
unset($options['end'], $options['format']); unset($options['end'], $options['format']);
} else {
$format = $options;
} }
$now = self::fromString(time(), $timezone); $now = self::fromString(time(), $timezone);
$inSeconds = self::fromString($dateTime, $timezone); $inSeconds = self::fromString($dateTime, $timezone);
$backwards = ($inSeconds > $now); $isFuture = ($inSeconds > $now);
$futureTime = $now; if ($isFuture) {
$pastTime = $inSeconds; $startTime = $now;
if ($backwards) { $endTime = $inSeconds;
$futureTime = $inSeconds; } else {
$pastTime = $now; $startTime = $inSeconds;
$endTime = $now;
} }
$diff = $futureTime - $pastTime; $diff = $endTime - $startTime;
if (!$diff) { if ($diff === 0) {
return __d('cake', 'just now', 'just now'); return __d('cake', 'just now', 'just now');
} }
if ($diff > abs($now - self::fromString($end))) { $isAbsoluteDate = $diff > abs($now - self::fromString($relativeEnd));
return sprintf($absoluteString, if ($isAbsoluteDate) {
(strpos($format, '%') === false) ? if (strpos($format, '%') === false) {
date($format, $inSeconds) : $date = date($format, $inSeconds);
self::_strftime($format, $inSeconds) } else {
); $date = self::_strftime($format, $inSeconds);
}
return sprintf($absoluteString, $date);
} }
$years = $months = $weeks = $days = $hours = $minutes = $seconds = 0;
// If more than a week, then take into account the length of months // If more than a week, then take into account the length of months
if ($diff >= 604800) { if ($diff >= 604800) {
list($future['H'], $future['i'], $future['s'], $future['d'], $future['m'], $future['Y']) = explode('/', date('H/i/s/d/m/Y', $futureTime)); list($future['H'], $future['i'], $future['s'], $future['d'], $future['m'], $future['Y']) = explode('/', date('H/i/s/d/m/Y', $endTime));
list($past['H'], $past['i'], $past['s'], $past['d'], $past['m'], $past['Y']) = explode('/', date('H/i/s/d/m/Y', $startTime));
list($past['H'], $past['i'], $past['s'], $past['d'], $past['m'], $past['Y']) = explode('/', date('H/i/s/d/m/Y', $pastTime));
$years = $months = $weeks = $days = $hours = $minutes = $seconds = 0;
$years = $future['Y'] - $past['Y']; $years = $future['Y'] - $past['Y'];
$months = $future['m'] + ((12 * $years) - $past['m']); $months = $future['m'] + ((12 * $years) - $past['m']);
@ -824,13 +833,13 @@ class CakeTime {
if ($future['d'] >= $past['d']) { if ($future['d'] >= $past['d']) {
$days = $future['d'] - $past['d']; $days = $future['d'] - $past['d'];
} else { } else {
$daysInPastMonth = date('t', $pastTime); $daysInPastMonth = date('t', $startTime);
$daysInFutureMonth = date('t', mktime(0, 0, 0, $future['m'] - 1, 1, $future['Y'])); $daysInFutureMonth = date('t', mktime(0, 0, 0, $future['m'] - 1, 1, $future['Y']));
if (!$backwards) { if ($isFuture) {
$days = ($daysInPastMonth - $past['d']) + $future['d'];
} else {
$days = ($daysInFutureMonth - $past['d']) + $future['d']; $days = ($daysInFutureMonth - $past['d']) + $future['d'];
} else {
$days = ($daysInPastMonth - $past['d']) + $future['d'];
} }
if ($future['m'] != $past['m']) { if ($future['m'] != $past['m']) {
@ -853,9 +862,7 @@ class CakeTime {
$days = $days - ($weeks * 7); $days = $days - ($weeks * 7);
} }
} else { } else {
$years = $months = $weeks = 0;
$days = floor($diff / 86400); $days = floor($diff / 86400);
$diff = $diff - ($days * 86400); $diff = $diff - ($days * 86400);
$hours = floor($diff / 3600); $hours = floor($diff / 3600);
@ -863,69 +870,58 @@ class CakeTime {
$minutes = floor($diff / 60); $minutes = floor($diff / 60);
$diff = $diff - ($minutes * 60); $diff = $diff - ($minutes * 60);
$seconds = $diff; $seconds = $diff;
} }
$fWord = $accuracy['second']; $accuracy = $accuracies['second'];
if ($years > 0) { if ($years > 0) {
$fWord = $accuracy['year']; $accuracy = $accuracies['year'];
} elseif (abs($months) > 0) { } elseif (abs($months) > 0) {
$fWord = $accuracy['month']; $accuracy = $accuracies['month'];
} elseif (abs($weeks) > 0) { } elseif (abs($weeks) > 0) {
$fWord = $accuracy['week']; $accuracy = $accuracies['week'];
} elseif (abs($days) > 0) { } elseif (abs($days) > 0) {
$fWord = $accuracy['day']; $accuracy = $accuracies['day'];
} elseif (abs($hours) > 0) { } elseif (abs($hours) > 0) {
$fWord = $accuracy['hour']; $accuracy = $accuracies['hour'];
} elseif (abs($minutes) > 0) { } elseif (abs($minutes) > 0) {
$fWord = $accuracy['minute']; $accuracy = $accuracies['minute'];
} }
$fNum = str_replace(array('year', 'month', 'week', 'day', 'hour', 'minute', 'second'), array(1, 2, 3, 4, 5, 6, 7), $fWord); $accuracyNum = str_replace(array('year', 'month', 'week', 'day', 'hour', 'minute', 'second'), array(1, 2, 3, 4, 5, 6, 7), $accuracy);
$relativeDate = ''; $relativeDate = [];
if ($fNum >= 1 && $years > 0) { if ($accuracyNum >= 1 && $years > 0) {
$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d year', '%d years', $years, $years); $relativeDate[] = __dn('cake', '%d year', '%d years', $years, $years);
} }
if ($fNum >= 2 && $months > 0) { if ($accuracyNum >= 2 && $months > 0) {
$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d month', '%d months', $months, $months); $relativeDate[] = __dn('cake', '%d month', '%d months', $months, $months);
} }
if ($fNum >= 3 && $weeks > 0) { if ($accuracyNum >= 3 && $weeks > 0) {
$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d week', '%d weeks', $weeks, $weeks); $relativeDate[] = __dn('cake', '%d week', '%d weeks', $weeks, $weeks);
} }
if ($fNum >= 4 && $days > 0) { if ($accuracyNum >= 4 && $days > 0) {
$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d day', '%d days', $days, $days); $relativeDate[] = __dn('cake', '%d day', '%d days', $days, $days);
} }
if ($fNum >= 5 && $hours > 0) { if ($accuracyNum >= 5 && $hours > 0) {
$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d hour', '%d hours', $hours, $hours); $relativeDate[] = __dn('cake', '%d hour', '%d hours', $hours, $hours);
} }
if ($fNum >= 6 && $minutes > 0) { if ($accuracyNum >= 6 && $minutes > 0) {
$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d minute', '%d minutes', $minutes, $minutes); $relativeDate[] = __dn('cake', '%d minute', '%d minutes', $minutes, $minutes);
} }
if ($fNum >= 7 && $seconds > 0) { if ($accuracyNum >= 7 && $seconds > 0) {
$relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d second', '%d seconds', $seconds, $seconds); $relativeDate[] = __dn('cake', '%d second', '%d seconds', $seconds, $seconds);
} }
$relativeDate = implode(', ', $relativeDate);
// When time has passed if ($relativeDate) {
if (!$backwards && $relativeDate) { $relativeString = ($isFuture) ? $relativeStringFuture : $relativeStringPast;
return sprintf($relativeString, $relativeDate); return sprintf($relativeString, $relativeDate);
} }
if (!$backwards) {
$aboutAgo = array(
'second' => __d('cake', 'about a second ago'),
'minute' => __d('cake', 'about a minute ago'),
'hour' => __d('cake', 'about an hour ago'),
'day' => __d('cake', 'about a day ago'),
'week' => __d('cake', 'about a week ago'),
'year' => __d('cake', 'about a year ago')
);
return $aboutAgo[$fWord]; if ($isFuture) {
} $strings = array(
// When time is to come
if (!$relativeDate) {
$aboutIn = array(
'second' => __d('cake', 'in about a second'), 'second' => __d('cake', 'in about a second'),
'minute' => __d('cake', 'in about a minute'), 'minute' => __d('cake', 'in about a minute'),
'hour' => __d('cake', 'in about an hour'), 'hour' => __d('cake', 'in about an hour'),
@ -933,11 +929,18 @@ class CakeTime {
'week' => __d('cake', 'in about a week'), 'week' => __d('cake', 'in about a week'),
'year' => __d('cake', 'in about a year') 'year' => __d('cake', 'in about a year')
); );
} else {
return $aboutIn[$fWord]; $strings = array(
'second' => __d('cake', 'about a second ago'),
'minute' => __d('cake', 'about a minute ago'),
'hour' => __d('cake', 'about an hour ago'),
'day' => __d('cake', 'about a day ago'),
'week' => __d('cake', 'about a week ago'),
'year' => __d('cake', 'about a year ago')
);
} }
return $relativeDate; return $strings[$accuracy];
} }
/** /**