Skip to content

Commit

Permalink
Add age filter
Browse files Browse the repository at this point in the history
  • Loading branch information
SpacePossum committed Nov 23, 2016
1 parent d757962 commit 46843e1
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 10 deletions.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
Provides additional filters and tests to be used with [Twig](http://twig.sensiolabs.org).

#### Filters
- **Age**<br/>
Calculates the time difference (age) between a date and the current date.
- **Bytes**<br/>
Formats a number of bytes with binary or SI prefix multiple, either automatically or by given symbol.
- **Date**<br/>
Expand Down Expand Up @@ -45,6 +47,46 @@ Add the package to your `composer.json`.

## Filters

### AgeFilter
###### GeckoPackages\Twig\Filters\AgeFilter
Calculates and returns the time difference (age) between a date and the current date.

You use an accuracy:

| symbol | accuracy |
| ------ | -------- |
| y | Year |
| d | Day |
| h | Hour |
| i | Minute |
| s | Seconds |

The default is `y`. Symbols are case insensitive.

#### Examples

```Twig
{# today is new \DateTime() #}
{{ today|date_modify("-36 hours")|age('d') }} day.
{# 1.5 day. #}
{{ today|date_modify("180 minutes")|age('h') }} hours.
{# -3 hours. note the "minus" "#}
{{ today|date_modify("-180 minutes")|age('i') }} minutes.
{# 180 minutes. #}
{{ today|date_modify("-180 minutes")|age('s') }} seconds.
{# 10800 seconds. #}
```

Pass a timezone as second argument to set for the date passed. \*<br/>
Pass a timezone as third argument for creating the current date. \*

<sub>* Pass `null` to use the default, `false` to leave unchanged.</sub>

### BytesFilter
###### GeckoPackages\Twig\Filters\BytesFilter
Formats a number of bytes to a specific SI or binary unit or in a auto (best match) way.
Expand Down
41 changes: 41 additions & 0 deletions docs/filters/age.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
### Age

Calculates the time difference (age) between a date and the current date.

Calculates and returns the time difference (age) between a date and the current date.

You use an accuracy:

| symbol | accuracy |
| ------ | -------- |
| y | Year |
| d | Day |
| h | Hour |
| i | Minute |
| s | Seconds |

The default is `y`. Symbols are case insensitive.

#### Examples

```Twig
{# today is new \DateTime() #}
{{ today|date_modify("-36 hours")|age('d') }} day.
{# 1.5 day. #}
{{ today|date_modify("180 minutes")|age('h') }} hours.
{# -3 hours. note the "minus" "#}
{{ today|date_modify("-180 minutes")|age('i') }} minutes.
{# 180 minutes. #}
{{ today|date_modify("-180 minutes")|age('s') }} seconds.
{# 10800 seconds. #}
```

Pass a timezone as second argument to set for the date passed. \*<br/>
Pass a timezone as third argument for creating the current date. \*

<sub>* Pass `null` to use the default, `false` to leave unchanged.</sub>
99 changes: 99 additions & 0 deletions src/Twig/Filters/AgeFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

/*
* This file is part of the GeckoPackages.
*
* (c) GeckoPackages https://github.com/GeckoPackages
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace GeckoPackages\Twig\Filters;

use Twig_Environment;

/**
* Calculate the time diff (age) between a date and the current date.
*
* @api
*
* @author SpacePossum
*/
class AgeFilter extends \Twig_SimpleFilter
{
public function __construct()
{
parent::__construct(
'age',
/**
* @param Twig_Environment $env
* @param \DateTime|\DateTimeInterface|string|null $date
* @param string $acc accuracy; any of 'y, d, h, i, s', default 'y'
* @param \DateTimeZone|string|null|false $timezoneDate timezone of date passed, null to use the default, false to leave unchanged
* @param \DateTimeZone|string|null|false $timezoneNow timezone of the current date, null to use the default, false to leave unchanged
*
* @return string
*/
function (Twig_Environment $env, $date, $acc = 'y', $timezoneDate = null, $timezoneNow = null) {
$now = time();

if (!is_string($acc)) {
throw new \Twig_Error_Runtime(sprintf(
'Accuracy must be string, got %s.',
is_object($acc) ? get_class($acc) : gettype($acc).(is_resource($acc) ? '' : '#'.$acc)
));
}

$date = twig_date_converter($env, $date, $timezoneDate);
$now = twig_date_converter($env, $now, $timezoneNow);
$diff = $now->diff($date, false);

switch (strtolower($acc)) {
case 'y':
$v =
$diff->y
+ ($diff->m / 12)
+ (($diff->d + (($diff->h + (($diff->i + ($diff->s / 60)) / 60)) / 24)) / 365)
;

break;
// case 'm' is not supported by design
case 'd':
$v =
(int) $diff->format('%a')
+ (($diff->h + (($diff->i + ($diff->s / 60)) / 60)) / 24)
;

break;
case 'h':
$v =
24 * (int) $diff->format('%a')
+ $diff->h
+ (($diff->i + ($diff->s / 60)) / 60)
;

break;
case 'i':
$v =
60 * 24 * (int) $diff->format('%a')
+ 60 * $diff->h
+ $diff->i
+ ($diff->s / 60)
;

break;
case 's':
return (int) $now->format('U') - (int) $date->format('U');
default:
throw new \Twig_Error_Runtime(sprintf('Accuracy must be any of "y, d, h, i, s", got "%s".', $acc));
}

// (int) cast for HHVM =< 3.9.10
// https://github.com/facebook/hhvm/pull/6134 / https://github.com/facebook/hhvm/issues/5537
return 1 === (int) $diff->invert ? $v : -1 * $v;
},
['needs_environment' => true]
);
}
}
17 changes: 17 additions & 0 deletions tests/Twig/Tests/AbstractTwigTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
<?php

/*
* This file is part of the GeckoPackages.
*
* (c) GeckoPackages https://github.com/GeckoPackages
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

/**
* @author SpacePossum
*
* @internal
*/
abstract class AbstractTwigTest extends \PHPUnit_Framework_TestCase
{
/**
Expand All @@ -10,6 +24,9 @@ protected function getLoaderMock()
return $this->getMockBuilder('Twig_LoaderInterface')->getMock();
}

/**
* @return Twig_Environment new Twig_Environment with mocked loader set
*/
protected function getEnvironment()
{
return new Twig_Environment($this->getLoaderMock());
Expand Down
3 changes: 1 addition & 2 deletions tests/Twig/Tests/Filters/AbstractFilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ protected function getFilter()

/**
* To be replace with ... on PHP 5.6+ @see http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list.new
*
*/
protected function callFilter()
{
$filter = $this->getFilter();

return call_user_func_array ($filter->getCallable(), func_get_args());
return call_user_func_array($filter->getCallable(), func_get_args());
}
}
57 changes: 57 additions & 0 deletions tests/Twig/Tests/Filters/AgeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

/*
* This file is part of the GeckoPackages.
*
* (c) GeckoPackages https://github.com/GeckoPackages
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

/**
* @author SpacePossum
*
* @internal
*/
final class AgeTest extends AbstractFilterTest
{
/**
* @expectedException Twig_Error_Runtime
* @expectedExceptionMessageRegExp #^Accuracy must be string, got NULL\#.$#
*/
public function testFilterInvalidAccType()
{
$this->callFilter($this->getEnvironment(), time(), null);
}

/**
* @expectedException Twig_Error_Runtime
* @expectedExceptionMessageRegExp #^Accuracy must be any of \"y, d, h, i, s\", got "invalid".$#
*/
public function testFilterInvalidAccValue()
{
$this->callFilter($this->getEnvironment(), time(), 'invalid');
}

/**
* @param int $expected
* @param string $acc @see AgeFilter
* @param string $input Input for \DateTime constructor
*
* @dataProvider provideCases
*/
public function testFilter($expected, $acc, $input)
{
$this->assertSame($expected, $this->callFilter($this->getEnvironment(), new \DateTime($input), $acc));
}

public function provideCases()
{
return [
[3, 'h', '-180 minutes'],
[180, 'i', '-180 minutes'],
[180 * 60, 's', '-180 minutes'],
];
}
}
43 changes: 35 additions & 8 deletions tests/Twig/Tests/Fixtures/filters/age.test
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,37 @@ Test for the "age" filter.
{{ age_1_hour_30_minutes|age('i') }} min.
{{ age_1_hour_30_minutes|age('h') }} hour.
{{ age_2_days_plus6_hours|age('d') }} day.
{{ age_4_yours|age('y') }} years.
{{ age_4_yours_3_4|age('y') }} years.
{{ age_4_years|age('y') }} years.
{{ age_4_years_3_4|age('Y') }} years.
--- scoped
{{ age_5_sec|age('i')|number_format(3, ',') }} minutes.
--- negative
{{ min_age_2_days_4_hours_3_mi_2_sec|age('d')|round }} days.
{{ min_age_2_days_4_hours_3_mi_2_sec|age('h')|round }} hours.
{{ min_age_2_days_4_hours_3_mi_2_sec|age('i')|round }} minutes.
{{ min_age_2_days_4_hours_3_mi_2_sec|age('s') }} seconds.
{{ min_age_180_minutes|age('h') }} hours.
{{ min_age_180_minutes|age('i') }} minutes.
{{ min_age_180_minutes|age('s') }} seconds.
{{ min_age_75_sec|age('i') }} minutes.
{{ min_age_75_sec|age('s') }} seconds.
@@@
{{ today|date_modify("-36 hours")|age('d') }} day.
{{ today|date_modify("180 minutes")|age('h') }} hours.
{{ today|date_modify("-180 minutes")|age('i') }} minutes.
{{ today|date_modify("-180 minutes")|age('s') }} seconds.

--DATA--
return array(
'age_5_sec' => new DateTime('-5 seconds'),
'age_1_hour_30_minutes' => new DateTime('-1 hour -30 minutes'),
'age_2_days_plus6_hours' => new DateTime('-2 days +6 hours'),
'age_4_yours' => new DateTime('-4 years'),
'age_4_yours_3_4' => new DateTime('-4 years -9 months'),
'min_age_180_minutes' => new DateTime('180 minutes'),
'today' => new \DateTime(),
'age_4_years_3_4' => new \DateTime('-4 years -9 months'),
'age_4_years' => new \DateTime('-4 years'),
'age_2_days_plus6_hours' => new \DateTime('-2 days +6 hours'),
'age_1_hour_30_minutes' => new \DateTime('-1 hour -30 minutes'),
'age_5_sec' => new \DateTime('-5 seconds'),
'min_age_2_days_4_hours_3_mi_2_sec' => new \DateTime('2 days 4 hours 3 minutes 2 seconds'),
'min_age_180_minutes' => new \DateTime('180 minutes'),
'min_age_75_sec' => new \DateTime('75 seconds'),
);
--EXPECT--
--- positive
Expand All @@ -34,5 +49,17 @@ return array(
--- scoped
0,083 minutes.
--- negative
-2 days.
-52 hours.
-3123 minutes.
-187382 seconds.
-3 hours.
-180 minutes.
-10800 seconds.
-1.25 minutes.
-75 seconds.
@@@
1.5 day.
-3 hours.
180 minutes.
10800 seconds.

0 comments on commit 46843e1

Please sign in to comment.