Skip to content

Commit f9279ce

Browse files
committed
refactor and make better
1 parent 55fee07 commit f9279ce

10 files changed

Lines changed: 920 additions & 574 deletions

File tree

composer.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
"license": "MIT",
66
"autoload": {
77
"psr-4": {
8-
"Withinboredom\\Time\\": "src/"
8+
"Withinboredom\\": "src/"
99
},
1010
"files": [
11-
"src/functions.php"
11+
"src/Time/functions.php"
1212
]
1313
},
1414
"authors": [
@@ -18,12 +18,15 @@
1818
}
1919
],
2020
"require": {
21-
"php": "^8.2"
21+
"php": "^8.4"
2222
},
2323
"require-dev": {
24-
"pestphp/pest": "^2.34.8",
24+
"pestphp/pest": "^v3.8.2",
2525
"laravel/pint": "^1.16.2"
2626
},
27+
"suggest": {
28+
"crell/serde": "^1.3.2"
29+
},
2730
"config": {
2831
"allow-plugins": {
2932
"pestphp/pest-plugin": true

composer.lock

Lines changed: 719 additions & 514 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

readme.md

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,75 @@
1-
# A really simple library for converting time
1+
# An advanced library offering type safety and identity with Durations
22

3-
This is a simple library for interacting with time durations.
4-
You can write your code to expect time and know that is what you have:
3+
## Identity
4+
5+
This library uses some tricks to intern values so that two times are always equal to one another,
6+
no matter the distance in time or space.
7+
8+
```php
9+
use Withinboredom\Time;
10+
use Withinboredom\Time\Unit;
11+
12+
$hour = Time::from(Unit::Hours, 1);
13+
$minutes = Time::from(Unit::Minutes, 60);
14+
15+
echo $hour === $minutes ? 'true' : 'false'
16+
// outputs: true
17+
```
18+
19+
## Type Safety
20+
21+
You can ensure nobody will accidentally confuse seconds with milliseconds or minutes with seconds:
522

623
```php
7-
function sleep(\Withinboredom\Time\Time $time): void {
8-
\sleep($time->as(\Withinboredom\Time\TimeUnit::Seconds));
24+
function sleep(Time $time): void {
25+
\sleep($time->as(Unit::Seconds));
926
}
1027

11-
sleep(\Withinboredom\Time\Minutes(5));
28+
// Helper functions are included so you can type less code:
29+
sleep(Minutes(5));
1230
```
1331

14-
## Equality
32+
## Conversions and Math
1533

16-
All values of the same time are always strongly equaled to each other:
34+
You can easily convert between units and even perform operations, like sorting and arithmetic:
1735

1836
```php
19-
\Withinboredom\Time\Minutes(60) === \Withinboredom\Time\Hours(1)
37+
// use the hour constant to get one hour
38+
$hour = Hour;
39+
40+
$hour = $hour->multiply(10)->add(Minutes(10)); // get 10:10 hours
41+
42+
$interval = $hour->toDateInterval();
43+
44+
echo Hours(10) < $hour ? 'true' : 'false';
45+
// output: true
2046
```
2147

22-
## Utilities
48+
## Support for Crell\Serde
2349

24-
There are also a few utility methods:
50+
You cannot serialize/deserialize/clone `Time` objects.
51+
However, if you use something like Serde, you can still serialize your value objects:
2552

26-
> ->add(Time)->subtract(Time): AnyTime
53+
```php
54+
class CacheItem {
55+
public function __construct(
56+
#[Field('expiration_in_seconds')]
57+
#[TimeAs(Unit::Seconds)]
58+
public Time $expiration,
59+
) {}
60+
}
2761

28-
Add and subtract durations.
62+
$serde = new SerdeCommon(handlers: new \Withinboredom\Time\SerdeExporter());
63+
$serde->serialize(new CacheItem(Minutes(5)), 'json');
64+
```
2965

30-
> ->toDateInterval(): DateInterval
66+
The above will be serialized (and deserialized) from:
3167

32-
Creates a date interval for use in other things.
68+
```json
69+
{
70+
"expiration_in_seconds": 300
71+
}
72+
```
3373

3474
## Units
3575

@@ -45,7 +85,7 @@ Creates a date interval for use in other things.
4585

4686
> Why not months/years?
4787
48-
There's no set days in a month/year, so it’s better to use `DateInterval` for those types of measures.
88+
There are no set days in a month/year, so it’s better to use `DateInterval` for those types of measures.
4989

5090
> Why does this exist?
5191

src/Time.php

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<?php
22

3-
namespace Withinboredom\Time;
3+
namespace Withinboredom;
44

55
use WeakReference;
6+
use Withinboredom\Time\Unit;
67

78
final class Time
89
{
@@ -28,17 +29,17 @@ public function __destruct()
2829

2930
private function __construct(private readonly int $nanoseconds)
3031
{
31-
if($this->nanoseconds < 0) {
32+
if ($this->nanoseconds < 0) {
3233
throw new \InvalidArgumentException('Time cannot be negative');
3334
}
3435
}
3536

36-
public static function from(TimeUnit $unit, float $value): Time
37+
public static function from(Unit $unit, float $value): Time
3738
{
3839
return self::getValue($unit->value * $value);
3940
}
4041

41-
public function as(TimeUnit $unit): float
42+
public function as(Unit $unit): float
4243
{
4344
return $this->nanoseconds / $unit->value;
4445
}
@@ -65,11 +66,11 @@ public function divide(float $value): Time
6566

6667
private function components(): array
6768
{
68-
$weeks = (int) $this->as(TimeUnit::Weeks);
69-
$days = (int) $this->as(TimeUnit::Days) - $weeks * 7;
70-
$hours = (int) $this->as(TimeUnit::Hours) - $weeks * 7 * 24 - $days * 24;
71-
$minutes = (int) $this->as(TimeUnit::Minutes) - $weeks * 7 * 24 * 60 - $days * 24 * 60 - $hours * 60;
72-
$seconds = (int) $this->as(TimeUnit::Seconds) - $weeks * 7 * 24 * 60 * 60 - $days * 24 * 60 * 60 - $hours * 60 * 60 - $minutes * 60;
69+
$weeks = (int) $this->as(Unit::Weeks);
70+
$days = (int) $this->as(Unit::Days) - $weeks * 7;
71+
$hours = (int) $this->as(Unit::Hours) - $weeks * 7 * 24 - $days * 24;
72+
$minutes = (int) $this->as(Unit::Minutes) - $weeks * 7 * 24 * 60 - $days * 24 * 60 - $hours * 60;
73+
$seconds = (int) $this->as(Unit::Seconds) - $weeks * 7 * 24 * 60 * 60 - $days * 24 * 60 * 60 - $hours * 60 * 60 - $minutes * 60;
7374

7475
return [$weeks, $days, $hours, $minutes, $seconds];
7576
}

src/Time/SerdeExporter.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace Withinboredom\Time;
4+
5+
use Crell\Serde\Attributes\Field;
6+
use Crell\Serde\DeformatterResult;
7+
use Crell\Serde\Deserializer;
8+
use Crell\Serde\PropertyHandler\Exporter;
9+
use Crell\Serde\PropertyHandler\Importer;
10+
use Crell\Serde\Serializer;
11+
use Withinboredom\Time;
12+
13+
class SerdeExporter implements Exporter, Importer
14+
{
15+
public function exportValue(Serializer $serializer, Field $field, mixed $value, mixed $runningValue): mixed
16+
{
17+
$typeField = $field->typeField;
18+
assert($typeField instanceof TimeAs);
19+
assert($value instanceof Time);
20+
21+
return $serializer->formatter->serializeInt($runningValue, $field, $value->as($typeField->unit));
22+
}
23+
24+
public function canExport(Field $field, mixed $value, string $format): bool
25+
{
26+
return $value instanceof Time && $field->typeField instanceof TimeAs;
27+
}
28+
29+
public function importValue(Deserializer $deserializer, Field $field, mixed $source): mixed
30+
{
31+
$typeField = $field->typeField;
32+
assert($typeField instanceof TimeAs);
33+
34+
$number = $deserializer->deformatter->deserializeInt($source, $field);
35+
36+
if ($number === DeformatterResult::Missing) {
37+
return null;
38+
}
39+
40+
return Time::from($typeField->unit, $number);
41+
}
42+
43+
public function canImport(Field $field, string $format): bool
44+
{
45+
return $field->typeField instanceof TimeAs;
46+
}
47+
}

src/Time/TimeAs.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Withinboredom\Time;
4+
5+
use Crell\AttributeUtils\SupportsScopes;
6+
use Crell\Serde\TypeField;
7+
use Withinboredom\Time;
8+
9+
#[\Attribute(\Attribute::TARGET_PROPERTY)]
10+
class TimeAs implements TypeField, SupportsScopes
11+
{
12+
public function __construct(public readonly Unit $unit, protected readonly array $scopes = []) {}
13+
14+
public function scopes(): array
15+
{
16+
return $this->scopes;
17+
}
18+
19+
public function acceptsType(string $type): bool
20+
{
21+
return is_a($type, Time::class, true);
22+
}
23+
24+
public function validate(mixed $value): bool
25+
{
26+
return true;
27+
}
28+
}

src/Time/Unit.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Withinboredom\Time;
4+
5+
if (!enum_exists(Unit::class)) {
6+
enum Unit: int
7+
{
8+
case Nanoseconds = 1;
9+
case Microseconds = 1_000;
10+
case Milliseconds = 1_000_000;
11+
case Seconds = 1_000_000_000;
12+
case Minutes = 60_000_000_000;
13+
case Hours = 3_600_000_000_000;
14+
case Days = 86_400_000_000_000;
15+
case Weeks = 604_800_000_000_000;
16+
}
17+
}
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,46 @@
22

33
namespace Withinboredom\Time;
44

5+
use Withinboredom\Time;
6+
57
function Nanoseconds(float $time): Time
68
{
7-
return Time::from(TimeUnit::Nanoseconds, $time);
9+
return Time::from(Unit::Nanoseconds, $time);
810
}
911

1012
function Microseconds(float $time): Time
1113
{
12-
return Time::from(TimeUnit::Microseconds, $time);
14+
return Time::from(Unit::Microseconds, $time);
1315
}
1416

1517
function Milliseconds(float $time): Time
1618
{
17-
return Time::from(TimeUnit::Milliseconds, $time);
19+
return Time::from(Unit::Milliseconds, $time);
1820
}
1921

2022
function Seconds(float $time): Time
2123
{
22-
return Time::from(TimeUnit::Seconds, $time);
24+
return Time::from(Unit::Seconds, $time);
2325
}
2426

2527
function Minutes(float $time): Time
2628
{
27-
return Time::from(TimeUnit::Minutes, $time);
29+
return Time::from(Unit::Minutes, $time);
2830
}
2931

3032
function Hours(float $time): Time
3133
{
32-
return Time::from(TimeUnit::Hours, $time);
34+
return Time::from(Unit::Hours, $time);
3335
}
3436

3537
function Days(float $time): Time
3638
{
37-
return Time::from(TimeUnit::Days, $time);
39+
return Time::from(Unit::Days, $time);
3840
}
3941

4042
function Weeks(float $time): Time
4143
{
42-
return Time::from(TimeUnit::Weeks, $time);
44+
return Time::from(Unit::Weeks, $time);
4345
}
4446

4547
define('Nanosecond', Nanoseconds(1));

src/TimeUnit.php

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)