Skip to content

Commit

Permalink
feat(formatter)!: duration() fraction secconds and new format synta…
Browse files Browse the repository at this point in the history
…x support (#108)
  • Loading branch information
LastDragon-ru authored Jan 1, 2024
2 parents 56b83fa + 2a9b500 commit 9bc90af
Show file tree
Hide file tree
Showing 13 changed files with 696 additions and 4 deletions.
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

0 comments on commit 9bc90af

Please sign in to comment.