PHPackages                             tiny-blocks/time - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [PSR &amp; Standards](/categories/psr-standards)
4. /
5. tiny-blocks/time

ActiveLibrary[PSR &amp; Standards](/categories/psr-standards)

tiny-blocks/time
================

Value Object representing time in an immutable and strict way, focused on safe parsing, formatting and normalization.

1.5.0(2mo ago)1471MITPHPPHP ^8.5CI passing

Since Feb 17Pushed 2mo agoCompare

[ Source](https://github.com/tiny-blocks/time)[ Packagist](https://packagist.org/packages/tiny-blocks/time)[ Docs](https://github.com/tiny-blocks/time)[ RSS](/packages/tiny-blocks-time/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (6)Dependencies (10)Versions (8)Used By (1)

Time
====

[](#time)

[![License](https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e)](LICENSE)

- [Overview](#overview)
- [Installation](#installation)
- [How to use](#how-to-use)
    - [Instant](#instant)
    - [Duration](#duration)
    - [Period](#period)
    - [DayOfWeek](#dayofweek)
    - [TimeOfDay](#timeofday)
    - [Timezone](#timezone)
    - [Timezones](#timezones)
- [License](#license)
- [Contributing](#contributing)

Overview
--------

[](#overview)

Value Objects representing time in an immutable and strict way, focused on safe parsing, formatting, normalization and temporal arithmetic.

Installation
------------

[](#installation)

```
composer require tiny-blocks/time
```

How to use
----------

[](#how-to-use)

The library provides immutable Value Objects for representing points in time, quantities of time and time intervals. All instants are normalized to UTC internally.

### Instant

[](#instant)

An `Instant` represents a single point on the timeline, always stored in UTC with microsecond precision.

#### Creating from the current moment

[](#creating-from-the-current-moment)

Captures the current moment with microsecond precision, normalized to UTC.

```
use TinyBlocks\Time\Instant;

$instant = Instant::now();

$instant->toIso8601();            # 2026-02-17T10:30:00+00:00
$instant->toUnixSeconds();        # 1771324200
$instant->toDateTimeImmutable();  # DateTimeImmutable (UTC, with microseconds)
```

#### Creating from a string

[](#creating-from-a-string)

Parses a date-time string with an explicit UTC offset. The value is normalized to UTC regardless of the original offset.

```
use TinyBlocks\Time\Instant;

$instant = Instant::fromString(value: '2026-02-17T13:30:00-03:00');

$instant->toIso8601();     # 2026-02-17T16:30:00+00:00
$instant->toUnixSeconds(); # 1771345800
```

#### Creating from a database timestamp

[](#creating-from-a-database-timestamp)

Parses a database date-time string as UTC, with or without microsecond precision (e.g. MySQL `DATETIME`or `DATETIME(6)`).

```
use TinyBlocks\Time\Instant;

$instant = Instant::fromString(value: '2026-02-17 08:27:21.106011');

$instant->toIso8601();                                    # 2026-02-17T08:27:21+00:00
$instant->toDateTimeImmutable()->format('Y-m-d H:i:s.u'); # 2026-02-17 08:27:21.106011
```

Also supports timestamps without fractional seconds:

```
use TinyBlocks\Time\Instant;

$instant = Instant::fromString(value: '2026-02-17 08:27:21');

$instant->toIso8601(); # 2026-02-17T08:27:21+00:00
```

#### Creating from Unix seconds

[](#creating-from-unix-seconds)

Creates an `Instant` from a Unix timestamp in seconds.

```
use TinyBlocks\Time\Instant;

$instant = Instant::fromUnixSeconds(seconds: 0);

$instant->toIso8601();     # 1970-01-01T00:00:00+00:00
$instant->toUnixSeconds(); # 0
```

#### Adding and subtracting time

[](#adding-and-subtracting-time)

Returns a new `Instant` shifted forward or backward by a `Duration`.

```
use TinyBlocks\Time\Instant;
use TinyBlocks\Time\Duration;

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

$instant->plus(duration: Duration::fromMinutes(minutes: 30))->toIso8601();  # 2026-02-17T10:30:00+00:00
$instant->plus(duration: Duration::fromHours(hours: 2))->toIso8601();       # 2026-02-17T12:00:00+00:00
$instant->minus(duration: Duration::fromSeconds(seconds: 60))->toIso8601(); # 2026-02-17T09:59:00+00:00
```

#### Measuring distance between instants

[](#measuring-distance-between-instants)

Returns the absolute `Duration` between two `Instant` objects.

```
use TinyBlocks\Time\Instant;

$start = Instant::fromString(value: '2026-02-17T10:00:00+00:00');
$end = Instant::fromString(value: '2026-02-17T11:30:00+00:00');

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

$duration->toSeconds(); # 5400
$duration->toMinutes(); # 90
$duration->toHours();   # 1
```

The result is always non-negative regardless of direction:

```
$end->durationUntil(other: $start)->toSeconds(); # 5400
```

#### Comparing instants

[](#comparing-instants)

Provides strict temporal ordering between two `Instant` instances.

```
use TinyBlocks\Time\Instant;

$earlier = Instant::fromString(value: '2026-02-17T10:00:00+00:00');
$later = Instant::fromString(value: '2026-02-17T10:30:00+00:00');

$earlier->isBefore(other: $later);         # true
$earlier->isAfter(other: $later);          # false
$earlier->isBeforeOrEqual(other: $later);  # true
$earlier->isAfterOrEqual(other: $later);   # false
$later->isAfter(other: $earlier);          # true
$later->isAfterOrEqual(other: $earlier);   # true
```

### Duration

[](#duration)

A `Duration` represents an immutable, unsigned quantity of time measured in seconds. It has no reference point on the timeline — it expresses only "how much" time.

#### Creating durations

[](#creating-durations)

```
use TinyBlocks\Time\Duration;

$zero    = Duration::zero();
$seconds = Duration::fromSeconds(seconds: 90);
$minutes = Duration::fromMinutes(minutes: 30);
$hours   = Duration::fromHours(hours: 2);
$days    = Duration::fromDays(days: 7);
```

All factories reject negative values:

```
Duration::fromMinutes(minutes: -5); # throws InvalidSeconds
```

#### Arithmetic

[](#arithmetic)

```
use TinyBlocks\Time\Duration;

$thirtyMinutes = Duration::fromMinutes(minutes: 30);
$fifteenMinutes = Duration::fromMinutes(minutes: 15);

$thirtyMinutes->plus(other: $fifteenMinutes)->toSeconds();  # 2700 (45 minutes)
$thirtyMinutes->minus(other: $fifteenMinutes)->toSeconds(); # 900 (15 minutes)
```

Subtraction that would produce a negative result throws an exception:

```
$fifteenMinutes->minus(other: $thirtyMinutes); # throws InvalidSeconds
```

#### Division

[](#division)

Returns the number of times one `Duration` fits wholly into another. The result is truncated toward zero:

```
use TinyBlocks\Time\Duration;

$total = Duration::fromMinutes(minutes: 90);
$slot = Duration::fromMinutes(minutes: 30);

$total->divide(other: $slot); # 3
```

Division by a zero `Duration` throws an exception:

```
$total->divide(other: Duration::zero()); # throws InvalidSeconds
```

#### Comparing durations

[](#comparing-durations)

```
use TinyBlocks\Time\Duration;

$short = Duration::fromMinutes(minutes: 15);
$long = Duration::fromHours(hours: 2);

$short->isLessThan(other: $long);    # true
$long->isGreaterThan(other: $short); # true
$short->isZero();                    # false
Duration::zero()->isZero();          # true
```

#### Converting to other units

[](#converting-to-other-units)

Conversions truncate toward zero when the duration is not an exact multiple:

```
use TinyBlocks\Time\Duration;

$duration = Duration::fromSeconds(seconds: 5400);

$duration->toSeconds(); # 5400
$duration->toMinutes(); # 90
$duration->toHours();   # 1
$duration->toDays();    # 0
```

### Period

[](#period)

A `Period` represents a half-open time interval `[from, to)` between two UTC instants. The start is inclusive and the end is exclusive.

#### Creating from two instants

[](#creating-from-two-instants)

```
use TinyBlocks\Time\Instant;
use TinyBlocks\Time\Period;

$period = Period::from(
    from: Instant::fromString(value: '2026-02-17T10:00:00+00:00'),
    to: Instant::fromString(value: '2026-02-17T11:00:00+00:00')
);

$period->from->toIso8601(); # 2026-02-17T10:00:00+00:00
$period->to->toIso8601();   # 2026-02-17T11:00:00+00:00
```

The start must be strictly before the end:

```
Period::from(from: $later, to: $earlier); # throws InvalidPeriod
```

#### Creating from a start and duration

[](#creating-from-a-start-and-duration)

```
use TinyBlocks\Time\Duration;
use TinyBlocks\Time\Instant;
use TinyBlocks\Time\Period;

$period = Period::startingAt(
    from: Instant::fromString(value: '2026-02-17T10:00:00+00:00'),
    duration: Duration::fromMinutes(minutes: 90)
);

$period->from->toIso8601(); # 2026-02-17T10:00:00+00:00
$period->to->toIso8601();   # 2026-02-17T11:30:00+00:00
```

#### Getting the duration

[](#getting-the-duration)

```
$period->duration()->toSeconds(); # 5400
$period->duration()->toMinutes(); # 90
```

#### Checking if an instant is contained

[](#checking-if-an-instant-is-contained)

The check is inclusive at the start and exclusive at the end:

```
use TinyBlocks\Time\Instant;

$period->contains(instant: Instant::fromString(value: '2026-02-17T10:00:00+00:00')); # true (start, inclusive)
$period->contains(instant: Instant::fromString(value: '2026-02-17T10:30:00+00:00')); # true (middle)
$period->contains(instant: Instant::fromString(value: '2026-02-17T11:30:00+00:00')); # false (end, exclusive)
```

#### Detecting overlap

[](#detecting-overlap)

Two half-open intervals `[A, B)` and `[C, D)` overlap when `A < D` and `C < B`:

```
use TinyBlocks\Time\Duration;
use TinyBlocks\Time\Instant;
use TinyBlocks\Time\Period;

$periodA = Period::startingAt(
    from: Instant::fromString(value: '2026-02-17T10:00:00+00:00'),
    duration: Duration::fromHours(hours: 1)
);
$periodB = Period::startingAt(
    from: Instant::fromString(value: '2026-02-17T10:30:00+00:00'),
    duration: Duration::fromHours(hours: 1)
);

$periodA->overlapsWith(other: $periodB); # true
$periodB->overlapsWith(other: $periodA); # true
```

Adjacent periods do not overlap:

```
use TinyBlocks\Time\Duration;
use TinyBlocks\Time\Instant;
use TinyBlocks\Time\Period;

$first = Period::startingAt(
    from: Instant::fromString(value: '2026-02-17T10:00:00+00:00'),
    duration: Duration::fromHours(hours: 1)
);
$second = Period::startingAt(
    from: Instant::fromString(value: '2026-02-17T11:00:00+00:00'),
    duration: Duration::fromHours(hours: 1)
);

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

### DayOfWeek

[](#dayofweek)

A `DayOfWeek` represents a day of the week following ISO 8601, where Monday is 1 and Sunday is 7.

#### Deriving from an Instant

[](#deriving-from-an-instant)

```
use TinyBlocks\Time\DayOfWeek;
use TinyBlocks\Time\Instant;

$instant = Instant::fromString(value: '2026-02-17T10:30:00+00:00');
$day = DayOfWeek::fromInstant(instant: $instant);

$day;        # DayOfWeek::Tuesday
$day->value; # 2
```

#### Checking weekday or weekend

[](#checking-weekday-or-weekend)

```
use TinyBlocks\Time\DayOfWeek;

DayOfWeek::Monday->isWeekday();   # true
DayOfWeek::Monday->isWeekend();   # false
DayOfWeek::Saturday->isWeekday(); # false
DayOfWeek::Saturday->isWeekend(); # true
```

#### Calculating forward distance

[](#calculating-forward-distance)

Returns the number of days forward from one day to another, always in the range `[0, 6]`. The distance is measured forward through the week:

```
use TinyBlocks\Time\DayOfWeek;

DayOfWeek::Monday->distanceTo(other: DayOfWeek::Wednesday); # 2
DayOfWeek::Friday->distanceTo(other: DayOfWeek::Monday);    # 3 (forward through Sat, Sun, Mon)
DayOfWeek::Monday->distanceTo(other: DayOfWeek::Monday);    # 0
```

### TimeOfDay

[](#timeofday)

A `TimeOfDay` represents a time of day (hour and minute) without date or timezone context. Values range from 00:00 to 23:59.

#### Creating from components

[](#creating-from-components)

```
use TinyBlocks\Time\TimeOfDay;

$time = TimeOfDay::from(hour: 8, minute: 30);

$time->hour;   # 8
$time->minute; # 30
```

#### Creating from a string

[](#creating-from-a-string-1)

Parses a string in `HH:MM` or `HH:MM:SS` format. When seconds are present, they are discarded:

```
use TinyBlocks\Time\TimeOfDay;

$time = TimeOfDay::fromString(value: '14:30');

$time->hour;   # 14
$time->minute; # 30
```

Also accepts the `HH:MM:SS` format commonly returned by databases:

```
use TinyBlocks\Time\TimeOfDay;

$time = TimeOfDay::fromString(value: '08:30:00');

$time->hour;       # 8
$time->minute;     # 30
$time->toString(); # 08:30
```

#### Deriving from an Instant

[](#deriving-from-an-instant-1)

Extracts the time of day from an `Instant` in UTC:

```
use TinyBlocks\Time\Instant;
use TinyBlocks\Time\TimeOfDay;

$instant = Instant::fromString(value: '2026-02-17T14:30:00+00:00');
$time = TimeOfDay::fromInstant(instant: $instant);

$time->hour;   # 14
$time->minute; # 30
```

#### Named constructors

[](#named-constructors)

```
use TinyBlocks\Time\TimeOfDay;

$midnight = TimeOfDay::midnight(); # 00:00
$noon = TimeOfDay::noon();         # 12:00
```

#### Comparing times

[](#comparing-times)

```
use TinyBlocks\Time\TimeOfDay;

$morning = TimeOfDay::from(hour: 8, minute: 0);
$afternoon = TimeOfDay::from(hour: 14, minute: 30);

$morning->isBefore(other: $afternoon);        # true
$morning->isAfter(other: $afternoon);         # false
$morning->isBeforeOrEqual(other: $afternoon); # true
$afternoon->isAfterOrEqual(other: $morning);  # true
```

#### Measuring distance between times

[](#measuring-distance-between-times)

Returns the `Duration` between two times. The second time must be after the first:

```
use TinyBlocks\Time\TimeOfDay;

$start = TimeOfDay::from(hour: 8, minute: 0);
$end = TimeOfDay::from(hour: 12, minute: 30);

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

$duration->toMinutes(); # 270
```

#### Converting to other representations

[](#converting-to-other-representations)

```
use TinyBlocks\Time\TimeOfDay;

$time = TimeOfDay::from(hour: 8, minute: 30);

$time->toMinutesSinceMidnight();  # 510
$time->toDuration()->toSeconds(); # 30600
$time->toString();                # 08:30
```

### Timezone

[](#timezone)

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

#### Creating from an identifier

[](#creating-from-an-identifier)

```
use TinyBlocks\Time\Timezone;

$timezone = Timezone::from(identifier: 'America/Sao_Paulo');

$timezone->value;      # America/Sao_Paulo
$timezone->toString(); # America/Sao_Paulo
```

#### Creating a UTC timezone

[](#creating-a-utc-timezone)

```
use TinyBlocks\Time\Timezone;

$timezone = Timezone::utc();

$timezone->value; # UTC
```

#### Converting to DateTimeZone

[](#converting-to-datetimezone)

```
use TinyBlocks\Time\Timezone;

$timezone = Timezone::from(identifier: 'Asia/Tokyo');
$dateTimeZone = $timezone->toDateTimeZone();

$dateTimeZone->getName(); # Asia/Tokyo
```

### Timezones

[](#timezones)

An immutable collection of `Timezone` objects.

#### Creating from objects

[](#creating-from-objects)

```
use TinyBlocks\Time\Timezone;
use TinyBlocks\Time\Timezones;

$timezones = Timezones::from(
    Timezone::from(identifier: 'America/Sao_Paulo'),
    Timezone::from(identifier: 'America/New_York'),
    Timezone::from(identifier: 'Asia/Tokyo')
);

$timezones->count(); # 3
```

#### Creating from strings

[](#creating-from-strings)

```
use TinyBlocks\Time\Timezones;

$timezones = Timezones::fromStrings('UTC', 'America/Sao_Paulo', 'Europe/London');

$timezones->count();     # 3
$timezones->toStrings(); # ["UTC", "America/Sao_Paulo", "Europe/London"]
```

#### Getting all timezones

[](#getting-all-timezones)

Returns all `Timezone` objects in the collection:

```
$timezones->all(); # [Timezone("UTC"), Timezone("America/Sao_Paulo"), Timezone("Europe/London")]
```

#### Finding a timezone by identifier

[](#finding-a-timezone-by-identifier)

Searches for a specific IANA identifier within the collection. Returns `null` if not found.

```
use TinyBlocks\Time\Timezones;

$timezones = Timezones::fromStrings('UTC', 'America/Sao_Paulo', 'Asia/Tokyo');

$timezones->findByIdentifier(iana: 'Asia/Tokyo');    # Timezone("Asia/Tokyo")
$timezones->findByIdentifier(iana: 'Europe/London'); # null
```

#### Finding a timezone by identifier with UTC fallback

[](#finding-a-timezone-by-identifier-with-utc-fallback)

Searches for a specific IANA identifier within the collection. Returns UTC if not found.

```
use TinyBlocks\Time\Timezones;

$timezones = Timezones::fromStrings('UTC', 'America/Sao_Paulo', 'Asia/Tokyo');

$timezones->findByIdentifierOrUtc(iana: 'Asia/Tokyo');    # Timezone("Asia/Tokyo")
$timezones->findByIdentifierOrUtc(iana: 'Europe/London'); # Timezone("UTC")
```

#### Checking if a timezone exists in the collection

[](#checking-if-a-timezone-exists-in-the-collection)

```
use TinyBlocks\Time\Timezones;

$timezones = Timezones::fromStrings('America/Sao_Paulo', 'Asia/Tokyo');

$timezones->contains(iana: 'Asia/Tokyo');       # true
$timezones->contains(iana: 'America/New_York'); # false
```

#### Getting all identifiers as strings

[](#getting-all-identifiers-as-strings)

Returns all timezone identifiers as plain strings:

```
use TinyBlocks\Time\Timezones;

$timezones = Timezones::fromStrings('UTC', 'America/Sao_Paulo', 'Europe/London');

$timezones->toStrings(); # ["UTC", "America/Sao_Paulo", "Europe/London"]
```

License
-------

[](#license)

Time is licensed under [MIT](LICENSE).

Contributing
------------

[](#contributing)

Please follow the [contributing guidelines](https://github.com/tiny-blocks/tiny-blocks/blob/main/CONTRIBUTING.md) to contribute to the project.

###  Health Score

43

—

FairBetter than 91% of packages

Maintenance86

Actively maintained with recent releases

Popularity12

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 87.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

6

Last Release

72d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b00a83241d2af272255f1755bf6aff48e77821c59ef623520176d9e828977241?d=identicon)[gustavofreze](/maintainers/gustavofreze)

---

Top Contributors

[![gustavofreze](https://avatars.githubusercontent.com/u/22873481?v=4)](https://github.com/gustavofreze "gustavofreze (7 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

---

Tags

datetimehacktoberfestiso-8601open-sourcephptimetiny-blocksutcvalue-objectpsrValue Objectvodatetimetimeimmutabletiny-blocksutciso-8601

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/tiny-blocks-time/health.svg)

```
[![Health](https://phpackages.com/badges/tiny-blocks-time/health.svg)](https://phpackages.com/packages/tiny-blocks-time)
```

###  Alternatives

[psr/container

Common Container Interface (PHP FIG PSR-11)

10.0k1.0B3.7k](/packages/psr-container)[psr/clock

Common interface for reading the clock.

642332.9M343](/packages/psr-clock)[aeon-php/calendar

PHP type safe, immutable calendar library

2079.7M16](/packages/aeon-php-calendar)[stella-maris/clock

A pre-release of the proposed PSR-20 Clock-Interface

7947.5M2](/packages/stella-maris-clock)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
