Re: [RFC] Duration class
| From: | Tim Düsterhus | Date: | Tue, 07 Jul 2026 14:49:52 +0000 |
| Subject: | Re: [RFC] Duration class | ||
| References: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-131794@lists.php.net to get a copy of this message | ||
Hi
On 2026-07-04 02:42, Pierre Joye wrote:
See my reply to Marc as to how theThe $nanoseconds argument in theI do consider it breaks the promise. Additionally, it forces the user to do mental calculation and remember an arbitrary bound of 999,999,999 that doesn't exist in any of the APIs you cited as inspiration. Java's Duration.ofSeconds(long, long) explicitly documents carrying excess nanoseconds into seconds with no cap, Duration.ofSeconds(2, 1_000_000_001) produces the same value as Duration.ofSeconds(3, 1). Rust's Duration::new does the same. Temporal doesn't even carry by default. Temporal.Duration.from({ hours: 200, minutes: 17 }) is valid and preserves both values as given. None of the three reject or restrict a sub-unit's magnitude the way this proposal does.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.
fromSeconds() constructor is consistent with the other constructors in taking a single semantic value. If you want the overflow behavior, use an addition.
For the Rust comparison, I could imagine that this is a deliberate choice to make the constructor infallible. Requiring users to Duration::new(seconds, nanos).expect("success") every time they want to create a Duration from (known-good) values would be pretty unergonomic. And similarly panicking for nanosecond overflow would be unreasonable in case user input makes it into the function. PHP’s (unchecked) exceptions provide a reasonable middle-ground here, because users would be able to cleanly handle the ValueError if they desire to do so.
Java would again be closer to PHP here, but I'm not as familiar with how the Exception hierarchy is typically structured there.
Yes, *inspiration*. We looked at those to see how others approach the problem, but of course built our own list of requirements and/or goals based on constraints imposed by PHP and also based on our experience of working with dates and times both in PHP and other programming languages. Best regards Tim DüsterhusA 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.If injectivity were the real goal, none of Java, Rust, or Temporal would be usable APIs by that standard, they're explicitly non-injective by design, and that hasn't stopped them from being the reference implementations you're citing as inspiration.