Skip to content

Commit

Permalink
feature #177 feat: add "duration" formatter (kbond)
Browse files Browse the repository at this point in the history
This PR was merged into the 2.x-dev branch.

Discussion
----------

feat: add "duration" formatter

I have a requirement to use the `Helper::formatDuration()` from `symfony/console` in twig templates. I thought this bundle would be a good place for this.

Copied much of the code from `symfony/console` and added translations. I'll leave to others to add translations for other locales.

```twig
{{ 0|duration }} {# < 1 sec #}
{{ 1|duration }} {# 1 sec #}
{{ 56|duration }} {# 56 secs #}
{{ 60|duration }} {# 1 min #}
```

Should I update the changelog?

For the docs, we should perhaps re-jig them a bit as it says _This bundle does one simple job_ - not anymore if this PR is merged.

Commits
-------

22e7ca8 feat: add "duration" formatter
  • Loading branch information
weaverryan committed Jul 31, 2023
2 parents d543bce + 22e7ca8 commit f0aef14
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 1 deletion.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ find a change that break's semver, please create an issue.*
- Remove `DateTimeFormatter::getDiffMessage()`
- Remove `DateTimeFormatter::getDateTimeObject()`
- Remove `DateTimeFormatter::getEmptyDiffMessage()`
- Add `time_diff` filter as an alias for `ago`
- Add `time_diff` Twig filter as an alias for `ago`
- Add `DateTimeFormatter::formatDuration()` and `duration` Twig filter

## [v1.20.0](https://github.com/knplabs/knptimebundle/releases/tag/v1.20.0)

Expand Down
41 changes: 41 additions & 0 deletions src/DateTimeFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,47 @@ public function formatDiff(
return $this->translator->trans('diff.empty', [], 'time', $locale);
}

/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @source https://github.com/symfony/symfony/blob/ad72245261792c6b5d2db821fcbd141b11095215/src/Symfony/Component/Console/Helper/Helper.php#L97
*/
public function formatDuration(float $seconds, string $locale = null): string
{
static $timeFormats = [
[0, 'duration.none'],
[1, 'duration.second'],
[2, 'duration.second', 1],
[60, 'duration.minute'],
[120, 'duration.minute', 60],
[3600, 'duration.hour'],
[7200, 'duration.hour', 3600],
[86400, 'duration.day'],
[172800, 'duration.day', 86400],
];

foreach ($timeFormats as $index => $format) {
if ($seconds >= $format[0]) {
if ((isset($timeFormats[$index + 1]) && $seconds < $timeFormats[$index + 1][0])
|| $index === \count($timeFormats) - 1
) {
if (2 === \count($format)) {
return $this->translator->trans($format[1], [], 'time', $locale);
}

return $this->translator->trans(
$format[1],
['%count%' => floor($seconds / $format[2])],
'time',
$locale
);
}
}
}

return $this->translator->trans('duration.none', [], 'time', $locale);
}

private static function formatDateTime(int|string|\DateTimeInterface|null $value): \DateTimeInterface
{
if ($value instanceof \DateTimeInterface) {
Expand Down
5 changes: 5 additions & 0 deletions src/Twig/Extension/TimeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public function getFilters(): array
[DateTimeFormatter::class, 'formatDiff'],
['is_safe' => ['html']]
),
new TwigFilter(
'duration',
[DateTimeFormatter::class, 'formatDuration'],
['is_safe' => ['html']]
),
];
}
}
37 changes: 37 additions & 0 deletions tests/DateTimeFormatterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,41 @@ public function getFormatDiffTests(): \Generator
yield ['+ 5 years', null, 'diff.in.year'];
yield ['now', null, 'diff.empty'];
}

/**
* @dataProvider formatTimeProvider
*
* @param int $secs
* @param string $expectedFormat
*/
public function testFormatDuration($secs, $expectedFormat): void
{
$this->assertEquals($expectedFormat, $this->formatter->formatDuration($secs));
}

public static function formatTimeProvider(): iterable
{
return [
[0, 'duration.none'],
[1, 'duration.second'],
[2, 'duration.second'],
[59, 'duration.second'],
[60, 'duration.minute'],
[61, 'duration.minute'],
[119, 'duration.minute'],
[120, 'duration.minute'],
[121, 'duration.minute'],
[3599, 'duration.minute'],
[3600, 'duration.hour'],
[7199, 'duration.hour'],
[7200, 'duration.hour'],
[7201, 'duration.hour'],
[86399, 'duration.hour'],
[86400, 'duration.day'],
[86401, 'duration.day'],
[172799, 'duration.day'],
[172800, 'duration.day'],
[172801, 'duration.day'],
];
}
}
12 changes: 12 additions & 0 deletions tests/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ public function testServiceWiring(): void

$this->assertStringContainsString('Yesterday: 1 day ago', $result);
$this->assertStringContainsString('Now: now', $result);

$this->assertStringContainsString('1 day ago', $result);
$this->assertStringContainsString('Zero: < 1 sec', $result);
$this->assertStringContainsString('Less than a second: < 1 sec', $result);
$this->assertStringContainsString('One second: 1 sec', $result);
$this->assertStringContainsString('Multiple seconds: 59 secs', $result);
$this->assertStringContainsString('One minute: 1 min', $result);
$this->assertStringContainsString('Multiple minutes: 59 mins', $result);
$this->assertStringContainsString('One hour: 1 hr', $result);
$this->assertStringContainsString('Multiple hours: 23 hrs', $result);
$this->assertStringContainsString('One day: 1 day', $result);
$this->assertStringContainsString('Multiple days: 99 days', $result);
}

public function testLocalTranslation(): void
Expand Down
11 changes: 11 additions & 0 deletions tests/fixtures/template.twig
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
Yesterday: {{ yesterday|ago }}
Now: {{ date()|time_diff }}

Zero: {{ 0|duration }}
Less than a second: {{ 0.1|duration }}
One second: {{ 1|duration }}
Multiple seconds: {{ 59|duration }}
One minute: {{ 60|duration }}
Multiple minutes: {{ 3599|duration }}
One hour: {{ 3600|duration }}
Multiple hours: {{ 86399|duration }}
One day: {{ 86400|duration }}
Multiple days: {{ 8554100|duration }}
20 changes: 20 additions & 0 deletions translations/time.en.xliff
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,26 @@
<source>diff.in.year</source>
<target>in 1 year|in %count% years</target>
</trans-unit>
<trans-unit id="14">
<source>duration.second</source>
<target>1 sec|%count% secs</target>
</trans-unit>
<trans-unit id="15">
<source>duration.minute</source>
<target>1 min|%count% mins</target>
</trans-unit>
<trans-unit id="16">
<source>duration.hour</source>
<target>1 hr|%count% hrs</target>
</trans-unit>
<trans-unit id="17">
<source>duration.day</source>
<target>1 day|%count% days</target>
</trans-unit>
<trans-unit id="18">
<source>duration.none</source>
<target>&lt; 1 sec</target>
</trans-unit>
</body>
</file>
</xliff>

0 comments on commit f0aef14

Please sign in to comment.