Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Formatter:duration() fraction secconds support #108

Merged
merged 6 commits into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions packages/formatter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,146 @@ The `$locale->decimal(123.454321)` is:

Please check [source code](./src/Formatter.php) to see available methods and [config example](defaults/config.php) to available settings 🤗

# Formats

Some methods like as `date()`/`datetime()`/etc have `$format` argument. The argument specifies not the format but the format name. So you can use the names and do not worry about real formats. It is very important when application big/grow. To specify available names and formats the package config should be published and customized.

```shell
php artisan vendor:publish --provider=LastDragon_ru\\LaraASP\\Formatter\\Provider --tag=config
```

[include:example]: ./docs/Examples/Config.php
[//]: # (start: a0315c77f2fd2868ad7a67f118ff4816a93add9ae6e7d35899828ddc32cfac37)
[//]: # (warning: Generated automatically. Do not edit.)

```php
<?php declare(strict_types = 1);

use Illuminate\Container\Container;
use Illuminate\Support\Facades\Date;
use LastDragon_ru\LaraASP\Dev\App\Example;
use LastDragon_ru\LaraASP\Formatter\Formatter;
use LastDragon_ru\LaraASP\Formatter\Package;

Example::config(Package::Name, [
'options' => [
Formatter::Date => 'default',
],
'all' => [
Formatter::Date => [
'default' => 'd MMM yyyy',
'custom' => 'yyyy/MM/dd',
],
],
'locales' => [
'ru_RU' => [
Formatter::Date => [
'custom' => 'dd.MM.yyyy',
],
],
],
]);

$datetime = Date::make('2023-12-30T20:41:40.000018+04:00');
$default = Container::getInstance()->make(Formatter::class);
$locale = $default->forLocale('ru_RU');

Example::dump($default->date($datetime));
Example::dump($default->date($datetime, 'custom'));
Example::dump($locale->date($datetime));
Example::dump($locale->date($datetime, 'custom'));
```

The `$default->date($datetime)` is:

```plain
"30 Dec 2023"
```

The `$default->date($datetime, 'custom')` is:

```plain
"2023/12/30"
```

The `$locale->date($datetime)` is:

```plain
"30 дек. 2023"
```

The `$locale->date($datetime, 'custom')` is:

```plain
"30.12.2023"
```

[//]: # (end: a0315c77f2fd2868ad7a67f118ff4816a93add9ae6e7d35899828ddc32cfac37)

# Duration

To format duration you can use built-in Intl formatter, but it doesn't support fraction seconds and have different format between locales (for example, `12345` seconds is `3:25:45` in `en_US` locale, and `12 345` in `ru_RU`). These reasons make difficult to use it in real applications. To make `duration()` more useful, the alternative syntax was added.

[include:docblock]: ./src/Utils/DurationFormatter.php
[//]: # (start: 8e359fc1ea71d4c4b58c4acdcd3289f180a89cbd39ebdbd10422908bd66b0268)
[//]: # (warning: Generated automatically. Do not edit.)

The syntax is the same as [ICU Date/Time format syntax](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax).

| Symbol | Meaning |
|--------|-------------------------------|
| `y` | years |
| `M` | months |
| `d` | days |
| `H` | hours |
| `m` | minutes |
| `s` | seconds |
| `S` | fractional seconds |
| `z` | negative sign (default `-`) |
| `'` | escape for text |
| `''` | two single quotes produce one |

[//]: # (end: 8e359fc1ea71d4c4b58c4acdcd3289f180a89cbd39ebdbd10422908bd66b0268)

[include:example]: ./docs/Examples/Duration.php
[//]: # (start: bb574f6b1315aa7b33a56d897b23ecc4d18dece9ea201b85b54154e144931d3b)
[//]: # (warning: Generated automatically. Do not edit.)

```php
<?php declare(strict_types = 1);

use Illuminate\Container\Container;
use LastDragon_ru\LaraASP\Dev\App\Example;
use LastDragon_ru\LaraASP\Formatter\Formatter;

$default = Container::getInstance()->make(Formatter::class); // For default app locale
$locale = $default->forLocale('ru_RU'); // For ru_RU locale

Example::dump($default->duration(123.454321));
Example::dump($locale->duration(123.4543));
Example::dump($locale->duration(1_234_543));
```

The `$default->duration(123.454321)` is:

```plain
"00:02:03.454"
```

The `$locale->duration(123.4543)` is:

```plain
"00:02:03.454"
```

The `$locale->duration(1234543)` is:

```plain
"342:55:43.000"
```

[//]: # (end: bb574f6b1315aa7b33a56d897b23ecc4d18dece9ea201b85b54154e144931d3b)

[include:file]: ../../docs/Shared/Contributing.md
[//]: # (start: 057ec3a599c54447e95d6dd2e9f0f6a6621d9eb75446a5e5e471ba9b2f414b89)
[//]: # (warning: Generated automatically. Do not edit.)
Expand Down
21 changes: 21 additions & 0 deletions packages/formatter/defaults/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use LastDragon_ru\LaraASP\Core\Utils\ConfigMerger;
use LastDragon_ru\LaraASP\Formatter\Formatter;
use LastDragon_ru\LaraASP\Formatter\Utils\DurationFormatter;

/**
* -----------------------------------------------------------------------------
Expand Down Expand Up @@ -36,6 +37,12 @@
*/
// Formatter::Time => 'custom',

/**
* Default custom duration format name, you can also use
* {@link NumberFormatter::DURATION} for built-in Intl format.
*/
// Formatter::Duration => 'custom',

/**
* Global Attributes for {@link NumberFormatter::setAttribute}
*/
Expand Down Expand Up @@ -79,6 +86,15 @@
// 'custom' => 'HH:mm:ss.SSS',
// ],

/**
* Custom duration format for all locales
*
* @see DurationFormatter
*/
// Formatter::Duration => [
// 'custom' => 'HH:mm:ss.SSS',
// ],

/**
* Intl properties for all locales (will be merged with `options`)
*/
Expand Down Expand Up @@ -107,6 +123,11 @@
// 'custom' => 'HH:mm:ss',
// ],
//
// // Custom duration format for specific Locale
// Formatter::Duration => [
// 'custom' => 'HH:mm:ss',
// ],
//
// // Intl properties for specific Locale (will be merged with all`)
// Formatter::Decimal => [
// Formatter::IntlSymbols => [],
Expand Down
35 changes: 35 additions & 0 deletions packages/formatter/docs/Examples/Config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php declare(strict_types = 1);

use Illuminate\Container\Container;
use Illuminate\Support\Facades\Date;
use LastDragon_ru\LaraASP\Dev\App\Example;
use LastDragon_ru\LaraASP\Formatter\Formatter;
use LastDragon_ru\LaraASP\Formatter\Package;

Example::config(Package::Name, [
'options' => [
Formatter::Date => 'default',
],
'all' => [
Formatter::Date => [
'default' => 'd MMM yyyy',
'custom' => 'yyyy/MM/dd',
],
],
'locales' => [
'ru_RU' => [
Formatter::Date => [
'custom' => 'dd.MM.yyyy',
],
],
],
]);

$datetime = Date::make('2023-12-30T20:41:40.000018+04:00');
$default = Container::getInstance()->make(Formatter::class);
$locale = $default->forLocale('ru_RU');

Example::dump($default->date($datetime));
Example::dump($default->date($datetime, 'custom'));
Example::dump($locale->date($datetime));
Example::dump($locale->date($datetime, 'custom'));
3 changes: 3 additions & 0 deletions packages/formatter/docs/Examples/Config.run
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

"${BASH_SOURCE%/*}/../../../../dev/artisan" dev:example "${BASH_SOURCE%.*}.php"
12 changes: 12 additions & 0 deletions packages/formatter/docs/Examples/Duration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php declare(strict_types = 1);

use Illuminate\Container\Container;
use LastDragon_ru\LaraASP\Dev\App\Example;
use LastDragon_ru\LaraASP\Formatter\Formatter;

$default = Container::getInstance()->make(Formatter::class); // For default app locale
$locale = $default->forLocale('ru_RU'); // For ru_RU locale

Example::dump($default->duration(123.454321));
Example::dump($locale->duration(123.4543));
Example::dump($locale->duration(1_234_543));
3 changes: 3 additions & 0 deletions packages/formatter/docs/Examples/Duration.run
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

"${BASH_SOURCE%/*}/../../../../dev/artisan" dev:example "${BASH_SOURCE%.*}.php"
29 changes: 25 additions & 4 deletions packages/formatter/src/Formatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace LastDragon_ru\LaraASP\Formatter;

use Closure;
use DateInterval;
use DateTimeInterface;
use DateTimeZone;
use Illuminate\Container\Container;
Expand All @@ -15,6 +16,7 @@
use LastDragon_ru\LaraASP\Formatter\Exceptions\FailedToCreateDateFormatter;
use LastDragon_ru\LaraASP\Formatter\Exceptions\FailedToCreateNumberFormatter;
use LastDragon_ru\LaraASP\Formatter\Exceptions\FailedToFormatValue;
use LastDragon_ru\LaraASP\Formatter\Utils\DurationFormatter;
use Locale;
use NumberFormatter;
use OutOfBoundsException;
Expand Down Expand Up @@ -100,13 +102,18 @@ class Formatter {
public const Ordinal = 'ordinal';

/**
* Options:
* - none
* Options (one of):
* - `int`: {@link NumberFormatter::DURATION}
* - `string`: the name of custom format
*
* Locales options:
* - {@link Formatter::IntlTextAttributes}
* - {@link Formatter::IntlAttributes}
* - {@link Formatter::IntlSymbols}
* - `string`: available only for custom formats: locale specific pattern
* (key: `locales.<locale>.duration.<format>` and/or default pattern
* (key: `all.duration.<format>`)
* {@link DurationFormatter}
*/
public const Duration = 'duration';

Expand Down Expand Up @@ -313,8 +320,22 @@ public function ordinal(?int $value): string {
return $this->formatValue(static::Ordinal, $value);
}

public function duration(?int $value): string {
return $this->formatValue(static::Duration, $value);
public function duration(DateInterval|float|int|null $value, string $format = null): string {
$type = static::Duration;
$format = $format ?: $this->getOptions($type);
$format = is_string($format)
? Cast::toString($this->getLocaleOptions($type, $format))
: $format;
$format ??= 'HH:mm:ss.SSS';
$value ??= 0;
$value = $value instanceof DateInterval
? DurationFormatter::getTimestamp($value)
: $value;
$value = is_string($format)
? (new DurationFormatter($format))->format($value)
: $this->formatValue($type, $value);

return $value;
}

public function time(
Expand Down
20 changes: 20 additions & 0 deletions packages/formatter/src/FormatterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,30 @@ public function testPercentConfig(): void {
}

public function testDuration(): void {
self::assertEquals('03:25:45.120', $this->formatter->duration(12_345.12));
self::assertEquals('03:25:45.001', $this->formatter->forLocale('ru_RU')->duration(12_345.0005));
}

public function testDurationConfig(): void {
config([
Package::Name.'.options.'.Formatter::Duration => NumberFormatter::DURATION,
]);

self::assertEquals('3:25:45', $this->formatter->duration(12_345));
self::assertEquals("12\u{00A0}345", $this->formatter->forLocale('ru_RU')->duration(12_345));
}

public function testDurationCustomFormat(): void {
config([
Package::Name.'.options.'.Formatter::Duration => 'custom',
Package::Name.'.all.'.Formatter::Duration.'.custom' => 'mm:ss',
Package::Name.'.all.'.Formatter::Duration.'.custom2' => 'H:mm:ss.SSS',
]);

self::assertEquals('02:03', $this->formatter->duration(123.456));
self::assertEquals('0:02:03.456', $this->formatter->duration(123.456, 'custom2'));
}

public function testTime(): void {
$time = DateTime::createFromFormat('H:i:s', '23:24:59') ?: null;

Expand Down
Loading
Loading