Skip to content

Commit 1c19b82

Browse files
authored
Release/1.4.0 (#5)
1 parent d5fb930 commit 1c19b82

File tree

15 files changed

+1768
-395
lines changed

15 files changed

+1768
-395
lines changed

README.md

Lines changed: 170 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
* [Instant](#instant)
99
* [Duration](#duration)
1010
* [Period](#period)
11+
* [DayOfWeek](#dayofweek)
12+
* [TimeOfDay](#timeofday)
1113
* [Timezone](#timezone)
1214
* [Timezones](#timezones)
1315
* [License](#license)
@@ -113,9 +115,9 @@ use TinyBlocks\Time\Duration;
113115

114116
$instant = Instant::fromString(value: '2026-02-17T10:00:00+00:00');
115117

116-
$instant->plus(duration: Duration::ofMinutes(minutes: 30))->toIso8601(); # 2026-02-17T10:30:00+00:00
117-
$instant->plus(duration: Duration::ofHours(hours: 2))->toIso8601(); # 2026-02-17T12:00:00+00:00
118-
$instant->minus(duration: Duration::ofSeconds(seconds: 60))->toIso8601(); # 2026-02-17T09:59:00+00:00
118+
$instant->plus(duration: Duration::fromMinutes(minutes: 30))->toIso8601(); # 2026-02-17T10:30:00+00:00
119+
$instant->plus(duration: Duration::fromHours(hours: 2))->toIso8601(); # 2026-02-17T12:00:00+00:00
120+
$instant->minus(duration: Duration::fromSeconds(seconds: 60))->toIso8601(); # 2026-02-17T09:59:00+00:00
119121
```
120122

121123
#### Measuring distance between instants
@@ -130,15 +132,15 @@ $end = Instant::fromString(value: '2026-02-17T11:30:00+00:00');
130132

131133
$duration = $start->durationUntil(other: $end);
132134

133-
$duration->seconds; # 5400
135+
$duration->toSeconds(); # 5400
134136
$duration->toMinutes(); # 90
135137
$duration->toHours(); # 1
136138
```
137139

138140
The result is always non-negative regardless of direction:
139141

140142
```php
141-
$end->durationUntil(other: $start)->seconds; # 5400
143+
$end->durationUntil(other: $start)->toSeconds(); # 5400
142144
```
143145

144146
#### Comparing instants
@@ -170,43 +172,62 @@ timeline — it expresses only "how much" time.
170172
use TinyBlocks\Time\Duration;
171173

172174
$zero = Duration::zero();
173-
$seconds = Duration::ofSeconds(seconds: 90);
174-
$minutes = Duration::ofMinutes(minutes: 30);
175-
$hours = Duration::ofHours(hours: 2);
176-
$days = Duration::ofDays(days: 7);
175+
$seconds = Duration::fromSeconds(seconds: 90);
176+
$minutes = Duration::fromMinutes(minutes: 30);
177+
$hours = Duration::fromHours(hours: 2);
178+
$days = Duration::fromDays(days: 7);
177179
```
178180

179181
All factories reject negative values:
180182

181183
```php
182-
Duration::ofMinutes(minutes: -5); # throws InvalidDuration
184+
Duration::fromMinutes(minutes: -5); # throws InvalidSeconds
183185
```
184186

185187
#### Arithmetic
186188

187189
```php
188190
use TinyBlocks\Time\Duration;
189191

190-
$a = Duration::ofMinutes(minutes: 30);
191-
$b = Duration::ofMinutes(minutes: 15);
192+
$thirtyMinutes = Duration::fromMinutes(minutes: 30);
193+
$fifteenMinutes = Duration::fromMinutes(minutes: 15);
192194

193-
$a->plus(other: $b)->seconds; # 2700 (45 minutes)
194-
$a->minus(other: $b)->seconds; # 900 (15 minutes)
195+
$thirtyMinutes->plus(other: $fifteenMinutes)->toSeconds(); # 2700 (45 minutes)
196+
$thirtyMinutes->minus(other: $fifteenMinutes)->toSeconds(); # 900 (15 minutes)
195197
```
196198

197199
Subtraction that would produce a negative result throws an exception:
198200

199201
```php
200-
$b->minus(other: $a); # throws InvalidDuration
202+
$fifteenMinutes->minus(other: $thirtyMinutes); # throws InvalidSeconds
203+
```
204+
205+
#### Division
206+
207+
Returns the number of times one `Duration` fits wholly into another. The result is truncated toward zero:
208+
209+
```php
210+
use TinyBlocks\Time\Duration;
211+
212+
$total = Duration::fromMinutes(minutes: 90);
213+
$slot = Duration::fromMinutes(minutes: 30);
214+
215+
$total->divide(other: $slot); # 3
216+
```
217+
218+
Division by a zero `Duration` throws an exception:
219+
220+
```php
221+
$total->divide(other: Duration::zero()); # throws InvalidSeconds
201222
```
202223

203224
#### Comparing durations
204225

205226
```php
206227
use TinyBlocks\Time\Duration;
207228

208-
$short = Duration::ofMinutes(minutes: 15);
209-
$long = Duration::ofHours(hours: 2);
229+
$short = Duration::fromMinutes(minutes: 15);
230+
$long = Duration::fromHours(hours: 2);
210231

211232
$short->isLessThan(other: $long); # true
212233
$long->isGreaterThan(other: $short); # true
@@ -221,8 +242,9 @@ Conversions truncate toward zero when the duration is not an exact multiple:
221242
```php
222243
use TinyBlocks\Time\Duration;
223244

224-
$duration = Duration::ofSeconds(seconds: 5400);
245+
$duration = Duration::fromSeconds(seconds: 5400);
225246

247+
$duration->toSeconds(); # 5400
226248
$duration->toMinutes(); # 90
227249
$duration->toHours(); # 1
228250
$duration->toDays(); # 0
@@ -239,7 +261,7 @@ end is exclusive.
239261
use TinyBlocks\Time\Instant;
240262
use TinyBlocks\Time\Period;
241263

242-
$period = Period::of(
264+
$period = Period::from(
243265
from: Instant::fromString(value: '2026-02-17T10:00:00+00:00'),
244266
to: Instant::fromString(value: '2026-02-17T11:00:00+00:00')
245267
);
@@ -251,7 +273,7 @@ $period->to->toIso8601(); # 2026-02-17T11:00:00+00:00
251273
The start must be strictly before the end:
252274

253275
```php
254-
Period::of(from: $later, to: $earlier); # throws InvalidPeriod
276+
Period::from(from: $later, to: $earlier); # throws InvalidPeriod
255277
```
256278

257279
#### Creating from a start and duration
@@ -263,7 +285,7 @@ use TinyBlocks\Time\Period;
263285

264286
$period = Period::startingAt(
265287
from: Instant::fromString(value: '2026-02-17T10:00:00+00:00'),
266-
duration: Duration::ofMinutes(minutes: 90)
288+
duration: Duration::fromMinutes(minutes: 90)
267289
);
268290

269291
$period->from->toIso8601(); # 2026-02-17T10:00:00+00:00
@@ -273,7 +295,7 @@ $period->to->toIso8601(); # 2026-02-17T11:30:00+00:00
273295
#### Getting the duration
274296

275297
```php
276-
$period->duration()->seconds; # 5400
298+
$period->duration()->toSeconds(); # 5400
277299
$period->duration()->toMinutes(); # 90
278300
```
279301

@@ -300,11 +322,11 @@ use TinyBlocks\Time\Period;
300322

301323
$periodA = Period::startingAt(
302324
from: Instant::fromString(value: '2026-02-17T10:00:00+00:00'),
303-
duration: Duration::ofHours(hours: 1)
325+
duration: Duration::fromHours(hours: 1)
304326
);
305327
$periodB = Period::startingAt(
306328
from: Instant::fromString(value: '2026-02-17T10:30:00+00:00'),
307-
duration: Duration::ofHours(hours: 1)
329+
duration: Duration::fromHours(hours: 1)
308330
);
309331

310332
$periodA->overlapsWith(other: $periodB); # true
@@ -320,16 +342,138 @@ use TinyBlocks\Time\Period;
320342

321343
$first = Period::startingAt(
322344
from: Instant::fromString(value: '2026-02-17T10:00:00+00:00'),
323-
duration: Duration::ofHours(hours: 1)
345+
duration: Duration::fromHours(hours: 1)
324346
);
325347
$second = Period::startingAt(
326348
from: Instant::fromString(value: '2026-02-17T11:00:00+00:00'),
327-
duration: Duration::ofHours(hours: 1)
349+
duration: Duration::fromHours(hours: 1)
328350
);
329351

330352
$first->overlapsWith(other: $second); # false
331353
```
332354

355+
### DayOfWeek
356+
357+
A `DayOfWeek` represents a day of the week following ISO 8601, where Monday is 1 and Sunday is 7.
358+
359+
#### Deriving from an Instant
360+
361+
```php
362+
use TinyBlocks\Time\DayOfWeek;
363+
use TinyBlocks\Time\Instant;
364+
365+
$instant = Instant::fromString(value: '2026-02-17T10:30:00+00:00');
366+
$day = DayOfWeek::fromInstant(instant: $instant);
367+
368+
$day; # DayOfWeek::Tuesday
369+
$day->value; # 2
370+
```
371+
372+
#### Checking weekday or weekend
373+
374+
```php
375+
use TinyBlocks\Time\DayOfWeek;
376+
377+
DayOfWeek::Monday->isWeekday(); # true
378+
DayOfWeek::Monday->isWeekend(); # false
379+
DayOfWeek::Saturday->isWeekday(); # false
380+
DayOfWeek::Saturday->isWeekend(); # true
381+
```
382+
383+
### TimeOfDay
384+
385+
A `TimeOfDay` represents a time of day (hour and minute) without date or timezone context. Values range from 00:00 to
386+
23:59.
387+
388+
#### Creating from components
389+
390+
```php
391+
use TinyBlocks\Time\TimeOfDay;
392+
393+
$time = TimeOfDay::from(hour: 8, minute: 30);
394+
395+
$time->hour; # 8
396+
$time->minute; # 30
397+
```
398+
399+
#### Creating from a string
400+
401+
Parses a string in `HH:MM` format:
402+
403+
```php
404+
use TinyBlocks\Time\TimeOfDay;
405+
406+
$time = TimeOfDay::fromString(value: '14:30');
407+
408+
$time->hour; # 14
409+
$time->minute; # 30
410+
```
411+
412+
#### Deriving from an Instant
413+
414+
Extracts the time of day from an `Instant` in UTC:
415+
416+
```php
417+
use TinyBlocks\Time\Instant;
418+
use TinyBlocks\Time\TimeOfDay;
419+
420+
$instant = Instant::fromString(value: '2026-02-17T14:30:00+00:00');
421+
$time = TimeOfDay::fromInstant(instant: $instant);
422+
423+
$time->hour; # 14
424+
$time->minute; # 30
425+
```
426+
427+
#### Named constructors
428+
429+
```php
430+
use TinyBlocks\Time\TimeOfDay;
431+
432+
$midnight = TimeOfDay::midnight(); # 00:00
433+
$noon = TimeOfDay::noon(); # 12:00
434+
```
435+
436+
#### Comparing times
437+
438+
```php
439+
use TinyBlocks\Time\TimeOfDay;
440+
441+
$morning = TimeOfDay::from(hour: 8, minute: 0);
442+
$afternoon = TimeOfDay::from(hour: 14, minute: 30);
443+
444+
$morning->isBefore(other: $afternoon); # true
445+
$morning->isAfter(other: $afternoon); # false
446+
$morning->isBeforeOrEqual(other: $afternoon); # true
447+
$afternoon->isAfterOrEqual(other: $morning); # true
448+
```
449+
450+
#### Measuring distance between times
451+
452+
Returns the `Duration` between two times. The second time must be after the first:
453+
454+
```php
455+
use TinyBlocks\Time\TimeOfDay;
456+
457+
$start = TimeOfDay::from(hour: 8, minute: 0);
458+
$end = TimeOfDay::from(hour: 12, minute: 30);
459+
460+
$duration = $start->durationUntil(other: $end);
461+
462+
$duration->toMinutes(); # 270
463+
```
464+
465+
#### Converting to other representations
466+
467+
```php
468+
use TinyBlocks\Time\TimeOfDay;
469+
470+
$time = TimeOfDay::from(hour: 8, minute: 30);
471+
472+
$time->toMinutesSinceMidnight(); # 510
473+
$time->toDuration()->toSeconds(); # 30600
474+
$time->toString(); # 08:30
475+
```
476+
333477
### Timezone
334478

335479
A `Timezone` is a Value Object representing a single valid [IANA timezone](https://www.iana.org) identifier.

src/DayOfWeek.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TinyBlocks\Time;
6+
7+
/**
8+
* Represents a day of the week following ISO 8601, where Monday is 1 and Sunday is 7.
9+
*/
10+
enum DayOfWeek: int
11+
{
12+
case Monday = 1;
13+
case Tuesday = 2;
14+
case Wednesday = 3;
15+
case Thursday = 4;
16+
case Friday = 5;
17+
case Saturday = 6;
18+
case Sunday = 7;
19+
20+
/**
21+
* Derives the day of the week from an Instant.
22+
*
23+
* @param Instant $instant The point in time to extract the day from.
24+
* @return DayOfWeek The corresponding day of the week in UTC.
25+
*/
26+
public static function fromInstant(Instant $instant): DayOfWeek
27+
{
28+
$isoDay = (int)$instant->toDateTimeImmutable()->format('N');
29+
30+
return self::from($isoDay);
31+
}
32+
33+
/**
34+
* Checks whether this day falls on a weekday (Monday through Friday).
35+
*
36+
* @return bool True if this is a weekday.
37+
*/
38+
public function isWeekday(): bool
39+
{
40+
return $this->value <= 5;
41+
}
42+
43+
/**
44+
* Checks whether this day falls on a weekend (Saturday or Sunday).
45+
*
46+
* @return bool True if this is a weekend day.
47+
*/
48+
public function isWeekend(): bool
49+
{
50+
return $this->value >= 6;
51+
}
52+
}

0 commit comments

Comments
 (0)