Re: [RFC] Time\Instant and Time\Clock

From: Date: Sun, 04 Oct 2026 14:04:19 +0000
Subject: Re: [RFC] Time\Instant and Time\Clock
References: 1 2 3 4 5 6  Groups: php.internals 
Request: Send a blank email to internals+get-132781@lists.php.net to get a copy of this message
Hello Tim Düsterhus, > https://wiki.php.net/rfc/time_instant_class > ========= > Comment 2 > ========= ... snipped ... (Tim Düsterhus) >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. (Mirco Babin) >Is this unique point on the timelime of the universe concept somewhere >defined? For example in an IETF-RFC, academic paper or somewhere else? >Because it seems to me that timeline of the universe must have some >definition somewhere? The RFC does not have any reference to a formal >definition? (Andreas Heigl) >I'd say that is defined as Barycentric Coordinated Time (TCB). But >that is slightly off towards the Geocentric Coordinated Time (TCG) >by about 490 ms per year. Geocentric Coordinated Time and Terrestrial >Time (TT) again differ by a constant from one another. Though both are >linear counts of SI-seconds. > >But all that is obsoleted and irrelevant in the end as the Instant is >based on the epoch which is based on UTC minus leap-seconds. At least >as far as I understood Tim. (Tim Düsterhus) >Yes, or “POSIX time”, which avoids the overloaded term UTC that could >also refer to the UTC timezone. Timezones reintroduce “date and time” >as a concept, which is not relevant to a point in time. (Mirco Babin) Thank you to Andreas Heighl and to Tim Düsterhus for this answer. This clears a lot of my confusion. The RFC introduction currently states: > This RFC proposes the addition of a Time\Instant class representing > an absolute point on the timeline of the universe together with a > Time\Clock interface and a Time\Clock\SystemClock implementation for > obtaining the current Time\Instant. The term "timeline of the universe" made me believe it was something completely different, a new time concept. It did not make me think of the Unix-Epoch-int time concept. Can an extra explanation be added in the introduction near the aforementioned RFC paragraph? Something like: A \Time\Instant can be viewed as a replacement for the Unix-Epoch-int. With the difference that the base-date might not be 1970-01-01. And with the difference that the \Time\Instant can have more precision in the form of milliscondes, microseconds, nanoseconds. The base-date and extra precision are an implementation detail and not described further. This also means the full timerange of the \Time\Instant is at least that of the Unix-Epoch-int, but can be extended in the future. > ========= > 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. (Tim Düsterhus) >The RFC acknowledges that. Writing an adapter is easy, particularly >since the addition of the DateTimeImmutable::toInstant() method to >the proposal. (Mirco Babin) >I have demonstrated it is impossible to write an adapter. Given the >constraint that only the PSR-20 MyClock class can be changed, >call-sites must not be adjusted. Also the purpose of a PSR-20 Clock >is to never call new DateTimeImmutable() or time() anymore, so the >assumption there are new DateTimeImmutable() calls that can be adapted >is incorrect. (Andreas Heigl) >An adapter does never need to implement both interfaces. In essence an >adapter only needs to implement "the other" interface. A ClockAdapter >uses a ClockInterface to create an Instant whereas a >ClockInterfaceAdapter uses a Clock to create a DateTime. >2 classes that seem rather straight forward. (Tim Düsterhus) >Indeed. Here's an example implementation for both directions. >The NativeToPsr20 adapter truncates to microseconds and needs to >“invent” a timezone. The opinionated choices made there should be > correct for the vast majority of users: ```php <?php final readonly class Psr20ToNative implements \Time\Clock { public function __construct( private \Psr\Clock\ClockInterface $clock, ) {} public function now(): \Time\Instant { return $this->clock->now()->toInstant(); } } ?> ``` ```php <?php final readonly class NativeToPsr20 implements \Psr\Clock\ClockInterface { public function __construct( private \Time\Clock $clock, private \DateTimeZone $timezone = new \DateTimeZone('UTC'), ) {} public function now(): \DateTimeImmutable { $now = $this->clock->now(); return \DateTimeImmutable ::createFromTimestamp($now->getUnixTimestamp()) ->setMicrosecond(\intdiv($now->getNanoseconds(), 1_000)) ->setTimezone($this->timezone); } } ?> ``` (Mirco Babin) These adapters are not what I meant. My point concerns all applications that currently use the PSR-20-Clock. To clarify more, consider an application using the PSR-20 clock with a theoretical function like this: ```php <?php // // The original theoretical PSR-20-application // class MyApp_Clock implements \Psr\Clock\ClockInterface { public function now(): \DateTimeImmutable { return new \DateTimeImmutable(); } } function AFunctionOfTheApplication() { // $clock might also come from a Dependency Injection container. // Or be injected via parameters or via constructor injection. // It doesn't really matter. $clock = new MyApp_Clock(); // The $clock is injected into external libraries. These // libraries are not under the control of the application. // Also the libraries do not use the Dependency Injection // container of the application. Otherwise the DI container // would have beeen provided as parameter instead of the // \Psr\Clock\ClockInterface. $result1 = Library_Alfa_Do_Something($clock); $result2 = Library_Beta_Do_Something($clock); $result3 = Library_Gamma_Do_Something($clock); $result4 = Library_Theta_Do_Something($clock); } ?> ``` At some point in the future time, one of the 4 libraries switches to \Time\SystemClock. Let's assume it is Beta that adapts. How is this code then going to look? ```php <?php // // Modified theoretical PSR-20-application // function AFunctionOfTheApplication() { // $clock might also come from a Dependency Injection container. // Or be injected via parameters or via constructor injection. // It doesn't really matter. $clock = new MyApp_Clock(); // The $clock is injected into external libraries. These // libraries are not under the control of the application. // Also the libraries do not use the Dependency Injection // container of the application. Otherwise the DI container // would have beeen provided as parameter instead of the // \Psr\Clock\ClockInterface. $result1 = Library_Alfa_Do_Something($clock); // Or use the provided Psr20ToNative adapter. $clockInstant = new \Time\SystemClock(); $result2 = Library_Beta_Do_Something($clockInstant); $result3 = Library_Gamma_Do_Something($clock); $result4 = Library_Theta_Do_Something($clock); } ?> ``` I hope the pattern becomes clear. After the \Time\Clock fact the PSR-20-application will be split in 2 clocks. Carefully selecting the right clock for the right call. And the careful selection has to take place on every update of any external library. Because the external library might have switched to \Time\Clock at any moment. This is a maintenance disaster. The best solution I can think of, is to adjust one class, namely the MyApp_Clock which the application controls. And make it a *hybrid clock*, both a \Psr\Clock\ClockInterface and a \Time\Clock. In that case there is an one-time adjustment in one class and all else will work *without any modification*. ```php <?php // hybrid clock class MyApp_Clock implements \Psr\Clock\ClockInterface, \Time\Clock { public function now(): \DateTimeImmutable { return new \DateTimeImmutable(); } public function nowAsInstant(): \Time\Instant { return \Time\SystemClock::get()->nowAsInstant(); } } But because the \Time\Clock->now() conflicts, as I have demonstrated, with the \Psr\Clock\ClockInterface->now(), this one-time solution is not possible. Now assume this PSR-20-application is a big one, thousands of classes, millions line of code. This will become a PSR-20-application maintenance disaster. So I ask once again, consider renaming \Time\Clock->now() to something else. The 'now()' naming obsession is not worth this interface conflict and maintenance disaster for PSR-20-applications. This also means: Yes, I do consider all Psr's including PSR-20 to be part of Php, and that PSR-interfaces can't be ignored nor conflicted. Kind regards, Mirco Babin

« previous php.internals (#132781) next »