Re: [PHP-DEV] [RFC] Duration class

[email protected] (Tim Düsterhus)
Newsgroups php.internals
Message-ID <[email protected]>
Hi

On 7/3/26 18:29, Pierre Joye wrote:
> I like that new Duration, good new APIs. I was following the
> discussion and one thing keeps coming to my mind as an API mistake:
> 
>    public static function fromSeconds(int $seconds, int $nanoseconds = 0)
> 
> Let me break down my thinking:
> 
> fromSeconds implies I'm setting a duration in seconds. But there are
> two arguments — the second is nanoseconds, and milliseconds isn't
> representable at all. The name promises one unit and the signature
> quietly provides another.

Please see https://news-web.php.net/php.internals/131419; and the fourth 
paragraph in the “design considerations”.

The $nanoseconds argument in the `fromSeconds()` constructor is 
intentionally limited to “the number of nanoseconds in a second” which 
means that the constructor can be reasoned about taking (fractional) 
seconds - the base unit of Durations - as a fixed point decimal. I do 
not consider this a break in the promise.

> That's correct, but I don't think it applies here. The fromSeconds
> issue is a naming defect regardless of language: the method name
> promises "seconds" and the signature quietly also takes nanoseconds.

As mentioned above, the $nanoseconds parameter is intentionally limited, 
making the constructor consistent with the other constructors in that 
the “unit that is mentioned in the name” may overflow into the larger 
units to allow construction with the desired precision in a way that 
makes sense for the given use case.

Extending this pattern to e.g. `fromMinutes()` would result in 
`fromMinutes($minutes, $seconds = 0)` with $seconds being limited to 59.

However in the other direction, `fromMilliseconds($milliseconds, 
$microseconds = 0)`, with $microseconds <= 999 would feel weird, because 
one wouldn’t say 5 milliseconds and 500 microseconds, when they can just 
move the comma and say 5500 microseconds.

The seconds boundary is where the units shift from a “metric” factor of 
1000 to a factor of 60. Thus the special handling.

> PHP already supports named arguments, giving us the same clarity as
> Temporal object literal without borrowing any JS syntax:
> 
>    Duration::from(seconds: -30, nanoseconds: -500);
>    Duration::from(seconds: -30, nanoseconds: 500); // error
>    Duration::from(seconds: 30, nanoseconds: 500).negate();
> 
> A single ::from() with named arguments would be unambiguous, easy to
> remember, and remove the need for the whole family of from<Unit>

A design goal of the constructors was that they are injective functions, 
i.e. there must be at most one way of creating a Duration with a 
specific length from any given constructor. This is in order to make it 
easy to reason about the resulting value, without needing to perform 
(mental) calculations.

i.e. Duration::from(seconds: 1, milliseconds: 1500) would misleadingly 
look “between 1 and 2 seconds” at a glance. But when each of the 
“subunits” are restricted in magnitude, it would be very inconvenient to 
construct values with the appropriate precision, because it would 
require splitting inputs across the different parameters.

> btw, also you mentioned Java's time package as the closest thing to
> DatetimeImmutable etc. I find that package a pain to use for 99% of
> usages, if not 100%. It is not that I want to say php is not java.
> However, in the current specific situation, I would totally mention
> that :)

The Duration class is taking inspiration from Java, Rust, Golang, and 
JavaScript, but is not a direct copy of any of these. I would expect the 
same from any future scope additions to the new “time and date” API.

I'm also confident that we will manage to build an API that stay true to 
PHP being a pragmatic language, while at the same time being correct and 
powerful enough to correctly model complex use cases.

Best regards
Tim Düsterhus
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.