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
138140The 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.
170172use 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
179181All 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
188190use 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
197199Subtraction 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
206227use 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
222243use 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.
239261use TinyBlocks\Time\Instant;
240262use 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
251273The 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
335479A ` Timezone ` is a Value Object representing a single valid [ IANA timezone] ( https://www.iana.org ) identifier.
0 commit comments