Hi
On 2026-07-04 02:42, Pierre Joye wrote:
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.
I 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.
See my reply to Marc as to how the `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.
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.
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.
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üsterhus