Re: [RFC] Duration class
| From: | marc at mabe dot berlin | Date: | Tue, 23 Jun 2026 06:19:02 +0000 |
| Subject: | Re: [RFC] Duration class | ||
| References: | 1 2 3 4 5 6 7 8 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-131490@lists.php.net to get a copy of this message | ||
> On 22.6.2026 19:04 CEST Tim Düsterhus <tim@bastelstu.be> wrote:
>
>
> Hi
>
> Am 2026-06-22 09:01, schrieb marc@mabe.berlin:
> > * Go (https://pkg.go.dev/time#Duration)
> > Just int64 of nanoseconds - negative durations are a negative number
>
> A single integer doesn't work for PHP (see my reply to John).
>
> > * Java
> >
> > (https://github.com/frohoff/jdk8u-jdk/blob/master/src/share/classes/java/time/Duration.java)
> > Seconds + nanoseconds approachwithout negative flag. Negative durations
> > are represented by a negative number of seconds while nanoseconds are
> > guarantied between >= 0 and < NANOS_PER SECOND
>
> I tested their implementation and consider their representation of
> negative durations to be unintuitive. Using milliseconds instead of
> nanoseconds for readability of this example:
>
> A duration of negative 500 milliseconds is effectively represented as
> (seconds: -1, milliseconds: 500), which can easily be confused with
> negative 1.5 seconds when “naively” using each component. One needs to
> calculate
$seconds + 1_000 * $milliseconds to obtain the
> correct
> meaning, which in my opinion is an indicator that the components
> shouldn't have been split in the first place. Or one always needs to
> remember that negative seconds are “off by one”.
What you feel unintuitive is just addition of two numbers in different units "$seconds +
$nanoseconds * 1_000_000_000", which for me feels very natural.
With the negative flag it's more complicated "($negative ? -1 : 1) * $seconds +
$nanoseconds * 1_000_000_000" to obtain the correct meaning.
I could not find any wide used implementation of durations that has such a design decision of
separating out the negativity. I don't think it's a good approach adding it as-is directly
to PHP core instead of taking one of the well established approaches.
In the RFC you say
> Negative durations are represented by an explicit $negative property. This makes it easy to
> deal with absolute values by just ignoring the value of $negative.
That's just not true - you can't simply ignore the sign you have to deal with it no matter
what. it's changing your calculations, it'g getting rejected on passing it to other
functions.
On the same time the current API makes it harder to deal with negative durations as they can not be
constructed directly and are not allowed as operator arguments.
I totally get the reasoning behind Rust's choice to make it fully unsigned - no negative
durations.
Thinking more about that I see only two cases for negative durations:
1. result of calculation
2. difference of two points in times with direction
If we agree on "negative durations are meaningless" then we can disallow them (throw) and
for the second case we calculate the distance.
>
> > Or we support negative durations but than it should not be special and
> > following "normal" math. If you need an absolute duration make sure
> > it's positive ... Adding a abs() /
> > absolute() method (like in
> > Java).
>
> The proposal in the RFC *is* following “normal math”. All the methods
> correctly take negative values into account. A sign-magnitude
> representation is also by no means uncommon, IEEE-754 floating points
> are also using a sign-magnitude representation. Personally I find
> sign-magnitude to be the leak awkward given the (necessary) second +
> subsecond split.
The sign bit already exists in signed integers - PHP does not have unsigned int's.
>
> > Another small note in the naming of $seconds and
> > $nanoseconds ...
> > The one is the total number, while the other one is the fraction of the
> > unit. If we later on want to add more helper like total number of x and
> > more fraction of different units we have ambiguity naming here.
> > -> I would rename $seconds into
> > $totalSeconds so maybe later on we
> > can add `$seconds { get => $totalSeconds % 60 }
>
> I haven't yet checked with Derick on this, but given the third paragraph
> of the “Design Considerations” and Rust’s / Java’s implementation which
> also use $seconds, I would disagree here. The same issue would also
> exist when $milliseconds and $microsecond accessors would be added,
> because $nanoseconds would then need to be restricted to 1_000 using the
> same argument. In my opinion this kind of “convenience” accessors are
> better left to explicit and clearly named methods.
Java does not have property hooks (as far as I know) but PHP now has.
That's very powerful and makes it possible to hide implementation details without exposing
everything as getters.
$duration->seconds // 0-59
$duration->totalSeconds
$duration->nanoseconds // 0 - 999_999_999
$duration->totalNanoseconds
$duration->milliseconds // 0 - 999
$duration->totalMilliseconds
// maybe later
$duration->picoseconds // 0 - 999_999_999_999
$duration->totalPicosecond
As a user you don't need to care that much of the internal representation.
>
> Best regards
> Tim Düsterhus