Hi
On 2026-09-30 19:40, Mirco Babin wrote:
=========
Prologue
=========
Well, I do have 8 comments on the Request For Comments (RFC).
This is a lengthy post, be prepared for a long reading time.
Thank you! I'm fully prepared for lengthy emails and am happy to answer
any clarifying questions to make sure we're all on the same page with
regard to what is being proposed. Your email is also nicely structured,
which makes it easy to work through it.
=========
Comment 1
=========
Is \Time\Instant meant to be immutable? If so, please state that in
the RFC.
Yes. This was already implied since the stub specifies that it's a
`readonly` class, but I've just adjusted the RFC text to explicitly
include the word “immutable”.
=========
Comment 2
=========
The RFC states:
An Time\Instant carries no timezone. It represents a unique point
on the timeline of the universe.
Why is there no explicit or implicit timezone attached? The RFC reads
to me like there is an implicit timezone attached, namely the UTC
timezone. Especially the `toIso8601DateTimeString()` function which
always returns the 'Z' timezone identifier seems to imply an implicit
UTC timezone?
Timezones are a concept to translate a point in time (“Instant”) into
date and time as shown on a clock. Both 2026-09-30T20:13:30+02:00 and
2026-09-30T18:13:30+00:00 refer to the same point in time. The point in
time itself does not have a timezone. The ISO-8601 getter chooses to
represent the point in time with the UTC / Zulu timezone, because
specifying an offset is required for the “date and time” representation
to be unambiguous. It would be equally valid for it to select a random
offset each time you use it. Using UTC / a zero offset is just a
pragmatic choice for best interoperability.
=========
Comment 3
=========
Why is it called \Time\Instant? I find \Time\UtcInstant a better name,
which embeds the timeline unit used.
\Time\UtcInstant leaves room for future different timeline Instants
like:
- \Time\TaiInstant (International Atomic Time)
- \Time\Ut1Instant (Universal Time)
- \Time\TtInstant (Terrestrial Time)
UTC, TAI and the others are just different projections of a “point on
the timeline of the universe” and the `Instant` class is largely
agnostic to them. It differs from a perfect model of the timeline of the
universe in that leap seconds are ignored (see my reply to Andreas
https://news-web.php.net/php.internals/132600), since that is what the
computing industry converged on. Nevertheless, this brings us back to
comment (2): The differences between UTC, TAI and the others concern the
*clock* time. The point on the timeline of the universe remains the
same.
And of course `Time\Instant` is just the ergonomic name, matching the
precedent set in Java (`java.time.Instant`) and JavaScript
(`Temporal.Instant`).
=========
Comment 4
=========
The \Time\Clock interface conflicts with PSR-20 Clock interface, see
https://www.php-fig.org/psr/psr-20/ . A class can never implement both
interfaces at the same time. This will hinder adoption.
The RFC acknowledges that. Writing an adapter is easy, particularly
since the addition of the `DateTimeImmutable::toInstant()` method to the
proposal.
Consider renaming the `function now(): \Time\Instant` function to
`function nowAsInstant(): \Time\Instant`.
`now()` is the obvious name for the method. Using something else would
mean that the proposed API would still suffer from a decision made for
compatibility after everyone has migrated to it and PSR-20 is long
forgotten. The horizon we are planning with is *at least* the next 15
years, but we're hoping that the API holds up for even longer.
For the same reason the compatibility layer with `DateTimeImmutable` is
explicitly added to the *old API* so that it is possible to deprecate
and remove the old API without affecting the new one.
=========
Comment 5
=========
The RFC introduces a SystemClock class:
```php
<?php
final readonly class SystemClock implements \Time\Clock
{
public function __construct() {}
/**
* The current instant, at the resolution offered by the platform
* clock,
* which is generally coarser than nanoseconds.
*
* Returned values are not guaranteed to be monotonically
* increasing across calls.
*/
public function now(): \Time\Instant {}
}
?>
```
Why is SystemClock not a Singleton? Can there be multiple different
system clocks in practice? I believe a System Clock, with the emphasis
on "System" is a System Wide clock. This clock is not bound to PHP,
but to all processes running on the system, including Apache,
shell-scripts, NGINX and others.
Conceptually there is only one `SystemClock` and internally each
`SystemClock` object accesses the same clock source provided by the
operating system. But you are making a good point here: Replacing the
constructor with a static `::get()` method or similar would make that
explicit and would also allow for reducing memory usage when
`SystemClock` objects are obtained at different places in the code
(instead of being pulled from a dependency injection container). I'll
put this on my list to discuss with Derick (and am happy to receive
naming suggestions for such a static getter).
The RFC also states a FrozenClock for testing purposes. Well this
The `FrozenClock` is *not* part of the RFC. It is provided in the
non-normative part of the RFC as an example to showcase how the `Clock`
interface can be useful for testing purposes. Whether or not a
`FrozenClock` will be added to PHP itself is to be decided as part of a
future RFC (by different authors, as indicated in my email from
yesterday).
pattern is not always going to work. Especially when an application
(the main program) uses Composer libraries and wants to freeze time
for each and everyone, including the `vendor` directory. Because not
every library in the `vendor` directory uses SystemClock. They all
use `time()`, because that is currently the standard way to obtain
the System Clock. Time freezing must involve the `time()` function!
It is debatable whether `time()` is the standard way to access the
system clock. It lacks support for fractional seconds and has terrible
ergonomics due to returning a bare integer. It is certainly still in
common use, but I'd argue the current best practice is a
`DateTimeImmutable` object, ideally one you obtained from a PSR-20
clock.
I think `SystemClock` should be removed from this RFC, it deserves
a seperate RFC. That RFC should also explore a frozen `time()`
function. Meaning the output of `time()` can be frozen recursively.
Because of this probably the \Time\Clock interface should also
be moved to that new RFC.
The point of the new date and time API is to completely supersede the
existing date and time API with a proper API design matching best
practices. Superseding the existing API includes the `time()` function -
which I would say is already a legacy API as of *today*, since
`DateTimeImmutable` does everything `time()` does and it does it better
(except maybe for brevity).
=========
Comment 6
=========
Why does \Time\Instant not have a named constructor
`public function fromSystemClock(): \Time\Instant` ?
Why all the complexity of constructing a \Time\SystemClock and
calling `now()`? The System Clock is a Singleton and is always
available, as the `time()` function shows.
See https://news-web.php.net/php.internals/132594. “Injecting” a clock
would be the correct solution. If `SystemClock` was made a singleton
with a static getter as mentioned above (while still implementing the
interface), this would allow for:
SystemClock::get()->now()
for simple use cases and one-off scripts, which would be even shorter
than your proposed:
Instant::fromSystemClock()
=========
Comment 7
=========
The RFC states:
PHP currently offers two ways to represent such a point, and both
are unsatisfying for the general case. A plain int as returned by
time() discards all sub-second precision and carries no type
information, leaving the question of “is this seconds, milliseconds
or something entirely different” to documentation instead of the
type system.
I want to bring something else to the table. What if one wants *less*
precision? What if one does not want seconds, milliseconds,
microseconds, nanoseconds? How is that going to fit into the
type system?
The `Instant` represents a point in time as precisely as possible. If
for some reason you need less precision, you can adjust it using the
`->add()` and `->sub()` methods as desired. Please also see
https://news-web.php.net/php.internals/132621 for a possible future
scope `->truncateTo()` method, but …
As a practical example, take a public transportation coach. The coach
has a schedule which is expressed in *minutes*, never in *seconds*. The
same argument, only *minutes* precision, goes for flight departures
and arrivals. Also ferries express their times in *minutes*.
… using an Instant to represent a public transportation schedule is the
wrong abstraction: Public transportation works on “date and time” as
shown on a calendar and a clock and only becomes meaningful once you
involve timezones. This is particularly important when a DST changeover
happens or your jurisdiction changes timezone rules: Now the point on
the timeline of the universe where your bus departs is different, but
the time shown on your wristwatch has remained the same.
=========
Comment 8
=========
The RFC states:
Extensions may want to adjust their API to take Time\Instant
in addition to an int representing a Unix timestamp or an
object implementing DateTimeInterface.
With regard to the `DateTimeInterface`, I want to share that this
interface is an internal interface and can't be implemented by
user-space classes. See also issue #23543 at
https://github.com/php/php-src/issues/23543
To clarify, this does not work:
```php
<?php
class MyTime implements DateTimeInterface
{
}
// Fatal error: DateTimeInterface can't be implemented by user classes
?>
```
That is an accurate observation, but not relevant to the quoted part. I
think there might be a misunderstanding: What the quote is saying is
that if there is a function `foo(DateTimeInterface $when)` it should be
adjusted to `foo(DateTimeInterface|Instant $when)` so that it is
possible to use it with the new API without needing to first convert the
`Instant` into a `DateTimeImmutable`.
Best regards
Tim Düsterhus