Skip to content

thomasschiet/php-temporal

Repository files navigation

PHP Temporal

A faithful port of the TC39 Temporal API to PHP.

PHP Temporal provides a complete, modern date/time library with nanosecond precision, DST-aware arithmetic, and multi-calendar support — all without depending on \DateTime or any PHP extensions.

Features

  • All 11 Temporal typesPlainDate, PlainTime, PlainDateTime, Duration, Instant, ZonedDateTime, TimeZone, Calendar, PlainYearMonth, PlainMonthDay, Now
  • Fully immutable — every operation returns a new instance
  • Nanosecond precisionInstant and ZonedDateTime track time to the nanosecond
  • DST-awareZonedDateTime correctly handles daylight saving transitions and ambiguous/skipped times
  • ISO 8601 parsing — extended years, calendar annotations, UTC offset suffixes
  • Multi-calendar support — ISO 8601, Gregorian (with eras), and Buddhist calendars, extensible via CalendarProtocol
  • Typed exceptions — a 9-class exception hierarchy under Temporal\Exception
  • Zero runtime dependencies — pure PHP, no extensions required
  • 5,400+ tests — including 4,200+ from the TC39 test262 reference suite

Requirements

  • PHP 8.4 or 8.5

Installation

composer require php-temporal/php-temporal

Quick Start

Dates

use Temporal\PlainDate;

$date = PlainDate::from('2025-03-14');

$later = $date->add(['months' => 1, 'days' => 5]);
echo $later; // 2025-04-19

$duration = $date->until(PlainDate::from('2025-12-31'));
echo $duration->days; // 292

Times

use Temporal\PlainTime;

$time = PlainTime::from('09:30:00');

$later = $time->add(['minutes' => 90]);
echo $later; // 11:00:00

$rounded = $time->round(['smallestUnit' => 'minute', 'roundingIncrement' => 15]);
echo $rounded; // 09:30:00

Durations

use Temporal\Duration;

$d = Duration::from('P1Y2M3DT4H5M6S');
echo $d->years;  // 1
echo $d->months; // 2

$balanced = Duration::from(['hours' => 25])->balance('days');
echo $balanced; // P1DT1H

Instants and Time Zones

use Temporal\Instant;
use Temporal\ZonedDateTime;

$instant = Instant::fromEpochMilliseconds(1741939200000);
$zdt = $instant->toZonedDateTimeISO('Europe/Amsterdam');
echo $zdt->toPlainDate(); // 2025-03-14

$zdt = ZonedDateTime::from('2025-03-14T09:30:00[Europe/Amsterdam]');
echo $zdt->offset; // +01:00

// DST-aware arithmetic
$zdt2 = $zdt->add(['months' => 1]);

Current Time

use Temporal\Now;

$instant = Now::instant();
$date    = Now::plainDateISO('Europe/Amsterdam');
$time    = Now::plainTimeISO('America/New_York');
$tz      = Now::timeZoneId();

API Overview

Type Description
PlainDate Calendar date (year, month, day) without time or time zone
PlainTime Wall-clock time without date or time zone
PlainDateTime Date and time without time zone
Duration Length of time (years through nanoseconds)
Instant Exact moment on the UTC timeline (nanosecond precision)
ZonedDateTime Date/time in a specific time zone (DST-aware)
TimeZone IANA time zone or fixed UTC offset
Calendar Calendar system (ISO 8601, Gregorian, Buddhist)
PlainYearMonth Year and month without a day
PlainMonthDay Month and day without a year
Now Static helpers for the current date, time, and instant

Development

# Install dependencies
composer install

# Run core tests
./vendor/bin/phpunit --testsuite Temporal

# Run TC39 test262 reference tests
./vendor/bin/phpunit --testsuite test262

# Run all tests
./vendor/bin/phpunit

# Format, lint, and analyze
./vendor/bin/mago fmt
./vendor/bin/mago lint
./vendor/bin/mago analyze

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors