[PHP-DEV] [RFC] Duration class

Hi

Derick and I are proposing the introduction of a new `Time\Duration` class to represent “stop-watch” or “egg-timer” durations to improve the developer experience for APIs taking a timeout. We are specifically targeting PHP 8.6 for this RFC, since part of the motivation is improving the API of the new “Polling API” that already landed in PHP 8.6 (PHP: rfc:poll_api) before the “backwards compatibility” door closes with the feature freeze in two months.

This RFC is also intended to be a first part of a modernized date and time API in PHP, while being useful on its own. To that extent and given the deadline we hope to make, the proposed API is intentionally minimal and focused on functionality that we are relatively certain to:

1. Be correct, or
2. be requirement for future additions that cannot later be added without breaking compatibility.

We would therefore ask to keep the discussion focused on actual issues rather than additional “convenience functionality” that might require extensive discussion or thought.

All that said, you can find the RFC at: PHP: rfc:duration_class. It hopefully includes all the important explanation and also provides a rationale as to why we made the design decisions we made.

Best regards
Tim Düsterhus

On Thu, 18 Jun 2026 at 14:48, Tim Düsterhus <tim@bastelstu.be> wrote:

Hi

Derick and I are proposing the introduction of a new `Time\Duration`
class to represent “stop-watch” or “egg-timer” durations to improve the
developer experience for APIs taking a timeout. We are specifically
targeting PHP 8.6 for this RFC, since part of the motivation is
improving the API of the new “Polling API” that already landed in PHP
8.6 (PHP: rfc:poll_api) before the “backwards
compatibility” door closes with the feature freeze in two months.

This RFC is also intended to be a first part of a modernized date and
time API in PHP, while being useful on its own. To that extent and given
the deadline we hope to make, the proposed API is intentionally minimal
and focused on functionality that we are relatively certain to:

1. Be correct, or
2. be requirement for future additions that cannot later be added
without breaking compatibility.

We would therefore ask to keep the discussion focused on actual issues
rather than additional “convenience functionality” that might require
extensive discussion or thought.

All that said, you can find the RFC at:
PHP: rfc:duration_class. It hopefully includes all the
important explanation and also provides a rationale as to why we made
the design decisions we made.

Best regards
Tim Düsterhus

Hi Tim,

Thank you for proposing this! I think it would be a good addition, and
a move in the right direction for a better built-in date-time stuff.

I do have some questions:

- Is there a reason the highest unit is seconds not hours?
- The RFC specifies `Duration::fromIso8601String`, but there does not
seem to be a method to turn the duration itself back into an ISO 8601
string. is there a reason for that?
- I think it would be nice to have methods that return the total
duration as a specific unit as a floating point, e.e.g.,
`getTotalSeconds()`, was this considered?

Also, i wanted to point out `Psl\DateTime\Duration` that is basically
the same thing:

which was modeled after Rust's `std::time::Duration` (
Duration in std::time - Rust ) and
Java's `Duration` (
Duration (Java Platform SE 8 ) ).

Cheers,
Seifeddine.

Hi

Am 2026-06-18 16:18, schrieb Seifeddine Gmati:

- Is there a reason the highest unit is seconds not hours?

The highest unit supported by the constructors is hours (`Duration::fromHours()`). The internal representation uses a seconds + nanoseconds pair for the reasons outlined in the design considerations, similarly to Rust’s and Java’s Duration. 68 years on 32-bit systems and 21× the age of the universe on 64-bit systems should be plenty.

- The RFC specifies `Duration::fromIso8601String`, but there does not
seem to be a method to turn the duration itself back into an ISO 8601
string. is there a reason for that?

The best reason there probably is “the proposed API is intentionally minimal”.

There are probably also some decisions to be made for the re-stringification into ISO-8601: Should it stringify straight into `sprintf("PT%d.%09dS", $seconds, $nanoseconds)` or should it try to use “as large units as possible” (i.e. minutes and hours). The correct answer might depend on the use case and thus it's probably better to move this decision to the PHP 8.7 cycle :slight_smile:

- I think it would be nice to have methods that return the total
duration as a specific unit as a floating point, e.e.g.,
`getTotalSeconds()`, was this considered?

Derick and I haven't discussed this, but the “minimal API” applies here as well. Floating point versions we specifically left out (for now), because we didn't want to go into the precision / rounding question. As an example how many nanoseconds should `1.4` be? The internal representation of that float is `1.3999999999999999`.

Best regards
Tim Düsterhus

On Thu, Jun 18, 2026, 4:02 PM Tim Düsterhus <tim@bastelstu.be> wrote:

Derick and I haven’t discussed this, but the “minimal API” applies here
as well. Floating point versions we specifically left out (for now),
because we didn’t want to go into the precision / rounding question. As
an example how many nanoseconds should 1.4 be? The internal
representation of that float is 1.3999999999999999.

I think precision doesn’t matter here. The primary reason we added float total methods in Psl was to be able to bridge a Duration object to other APIs that accepted float $seconds ( mainly Revolt ), Rust and Java also both offer the floating point methods, and i think they make sense. Could be added in the future though.

Another nitpick: I think the namespace should be DateTime if this is supposed to be start of a new API that covers both date and time, so we don’t end up with two namespace later (which will share alot of things back and forth).

Cheers,
Seifeddine.

Hi

Am 2026-06-18 17:20, schrieb Seifeddine Gmati:

[…] float […] Could be added in the future though.

Yes, my reply was not intended to be a “we are definitely against floats”, but more a “floats are not completely obvious to handle, so we rather think about the consequences later”.

Another nitpick: I think the namespace should be `DateTime` if this is
supposed to be start of a new API that covers both date and time, so we
don't end up with two namespace later (which will share alot of things back
and forth).

The `Time` namespace matches that of most references we looked at. `std::time` in Rust, `java.time` in Java, `time` in Golang. JavaScript calling it Temporal is the “odd person out”. Personally I also think of “Time” as the generic concept that also includes “Dates”.

Thus the upcoming classes to also handle “calendar dates” and “time as shown on a clock” would be appropriately located in the `Time` namespace. Java uses `java.time.ZonedDateTime` for the closest equivalent to PHP’s existing `DateTimeImmutable` (and `java.time.Instant` for timezone-less points in time). See java.time (Java Platform SE 8 ) for a description of the class hierarchy and relationships.

Best regards
Tim Düsterhus

Hi Tim,

The only one concern I see is the method fromIso8601String.

Unfortunately the PHP has this weird constant that is called DATE_ISO8601 https://www.php.net/manual/en/class.datetimeinterface.php#datetimeinterface.constants.iso8601, that is not in fact proper standard.

I wanted to deprecate it few years ago, but the push back from community was strong, so I did suggest only deprecation of DATE_RFC7231.

So what kind of format this method will except, standard ISO, or PHP’s version of ISO? Maybe it’s worth to find another name?

Kind regards,

Jorg

Hi

Am 2026-06-18 18:33, schrieb Jorg Sowa:

So what kind of format this method will except, standard ISO, or PHP's
version of ISO? Maybe it's worth to find another name?

The method will accept a ISO-8601 “Period” string, just like `DateInterval::__construct()` does. The only restriction is that period strings accepted by the Time\Duration class may only contain the “time” components. In simplified terms: The given string must start with `PT`.

The format strings you are thinking about are those to format date-times, which is a different concern and which will become relevant when classes to represent dates and times are added in the future. Given those are intended to fix the issues of the current API, I expect them to exactly follow the relevant standards when referring to a standard. Just like the URI parsers in the new URI extension follow the WHATWG and RFC 3986 standards - and any difference is considered a bug. Given that namespaces for PHP’s standard library are a relatively new thing, this luckily allows us to fix issues by building an entirely new, well-designed API without breaking backwards compatibility and without needing to come up with weird names to avoid conflicts. This worked well for the new random API in 8.2, the new DOM API in 8.4 and the new URI API in 8.5 and we hope to continue that with the new date API starting in 8.6.

Best regards
Tim Düsterhus

Hi Tim

On 6/18/26 15:47, Tim Düsterhus wrote:

Hi

Derick and I are proposing the introduction of a new `Time\Duration` class to represent “stop-watch” or “egg-timer” durations to improve the developer experience for APIs taking a timeout. We are specifically targeting PHP 8.6 for this RFC, since part of the motivation is improving the API of the new “Polling API” that already landed in PHP 8.6 (PHP: rfc:poll_api) before the “backwards compatibility” door closes with the feature freeze in two months.

This RFC is also intended to be a first part of a modernized date and time API in PHP, while being useful on its own. To that extent and given the deadline we hope to make, the proposed API is intentionally minimal and focused on functionality that we are relatively certain to:

1. Be correct, or
2. be requirement for future additions that cannot later be added without breaking compatibility.

We would therefore ask to keep the discussion focused on actual issues rather than additional “convenience functionality” that might require extensive discussion or thought.

All that said, you can find the RFC at: PHP: rfc:duration_class. It hopefully includes all the important explanation and also provides a rationale as to why we made the design decisions we made.

Best regards
Tim Düsterhus

Hi Tim,

Rather than building a whole new date/time library, did you consider using the temporal_rs library instead? This library is used for the new Temporal API that is now available in Firefox and Chrome and is a browser standard.

The temporal_rs library is already used in V8, Boa and Kiesel. This means the library has a large user-base, that is actively developed, but also stable because it needs to adhere to the standards.

Regards,
Frederik

Hi

Am 2026-06-18 22:25, schrieb Frederik Bosch:

Rather than building a whole new date/time library, did you consider using the temporal_rs library instead? This library is used for the new Temporal API that is now available in Firefox and Chrome and is a browser standard.

We did not look specifically at temporal_rs (I wasn't aware of its existing until now), but we looked at JavaScript’s Temporal API - amongst others.

I'm not speaking for Derick here, but I do not believe it is possible to just take an API that was built for a different programming language and plug it into a different programming language without making any changes. Different programming languages have different capabilities, ecosystems and code styles and without adjusting the API to the respective language, it will just stick out like a sore thumb.

As an example, JavaScript’s Temporal.Duration.prototype.round is an overloaded function with one overload taking an “options object”. This is something that works well with JavaScript’s dynamic nature, but is something we are moving away from in PHP, because it doesn't interact well with explicit type-checked signatures that are a first-class citizen in PHP. Semantically PHP is much closer to Java than it is to JavaScript and I believe that Java’s `java.time.*` is a comparatively better fit than JavaScript’s Temporal, but even that cannot be taken as-is, because Java supports overloaded methods (e.g. java.time.Duration.minus()) whereas PHP does not.

The complicated part here is the API design, not the internal implementation, particularly since PHP already has a date API that supports all the relevant operations. It's just that the userland API really starts to show its age. So using temporal_rs under the hood, while exposing a PHP-specific API will not provide any meaningful benefit - and would on the contrary add a new dependency on Rust. I'm not saying that including Rust is a bad idea, but integrating Rust libraries nicely into the Zend Engine, for example to make them work with PHP’s `memory_limit` would be a significant undertaking by itself and not be something we can just do as a “don’t worry about it” implementation detail.

Best regards
Tim Düsterhus

Hi Tim and Derick,

I appreciate the way the Duration class has been introduced. It is intentionally minimal, which is a sensible approach, while still leaving room for future extensions.

However, I would advise against introducing Duration::add and Duration::sub methods. Instead, I would recommend providing a single method, Duration::sum(Duration ...$durations): self.

Given that a Duration already carries a sign and may therefore be either positive or negative, the presence of separate add and sub methods could create an implicit and potentially misleading notion of directional behavior.

In contrast, a sum method using variadic arguments would allow multiple Duration instances to be combined in a natural and consistent manner. It would also avoid implying any expectation regarding the resulting sign, which may legitimately be either positive or negative depending on the input values.

On Thu, Jun 18, 2026 at 11:22 PM Tim Düsterhus <tim@bastelstu.be> wrote:

Hi

Am 2026-06-18 22:25, schrieb Frederik Bosch:

Rather than building a whole new date/time library, did you consider
using the temporal_rs library instead? This library is used for the new
Temporal API that is now available in Firefox and Chrome and is
a browser standard.

We did not look specifically at temporal_rs (I wasn’t aware of its
existing until now), but we looked at JavaScript’s Temporal API -
amongst others.

I’m not speaking for Derick here, but I do not believe it is possible to
just take an API that was built for a different programming language and
plug it into a different programming language without making any
changes. Different programming languages have different capabilities,
ecosystems and code styles and without adjusting the API to the
respective language, it will just stick out like a sore thumb.

As an example, JavaScript’s Temporal.Duration.prototype.round is an
overloaded function with one overload taking an “options object”. This
is something that works well with JavaScript’s dynamic nature, but is
something we are moving away from in PHP, because it doesn’t interact
well with explicit type-checked signatures that are a first-class
citizen in PHP. Semantically PHP is much closer to Java than it is to
JavaScript and I believe that Java’s java.time.* is a comparatively
better fit than JavaScript’s Temporal, but even that cannot be taken
as-is, because Java supports overloaded methods (e.g.
java.time.Duration.minus()) whereas PHP does not.

The complicated part here is the API design, not the internal
implementation, particularly since PHP already has a date API that
supports all the relevant operations. It’s just that the userland API
really starts to show its age. So using temporal_rs under the hood,
while exposing a PHP-specific API will not provide any meaningful
benefit - and would on the contrary add a new dependency on Rust. I’m
not saying that including Rust is a bad idea, but integrating Rust
libraries nicely into the Zend Engine, for example to make them work
with PHP’s memory_limit would be a significant undertaking by itself
and not be something we can just do as a “don’t worry about it”
implementation detail.

Best regards
Tim Düsterhus

On Thu, Jun 18, 2026, at 8:47 AM, Tim Düsterhus wrote:

Hi

Derick and I are proposing the introduction of a new `Time\Duration`
class to represent “stop-watch” or “egg-timer” durations to improve the
developer experience for APIs taking a timeout. We are specifically
targeting PHP 8.6 for this RFC, since part of the motivation is
improving the API of the new “Polling API” that already landed in PHP
8.6 (PHP: rfc:poll_api) before the “backwards
compatibility” door closes with the feature freeze in two months.

This RFC is also intended to be a first part of a modernized date and
time API in PHP, while being useful on its own. To that extent and given
the deadline we hope to make, the proposed API is intentionally minimal
and focused on functionality that we are relatively certain to:

1. Be correct, or
2. be requirement for future additions that cannot later be added
without breaking compatibility.

We would therefore ask to keep the discussion focused on actual issues
rather than additional “convenience functionality” that might require
extensive discussion or thought.

All that said, you can find the RFC at:
PHP: rfc:duration_class. It hopefully includes all the
important explanation and also provides a rationale as to why we made
the design decisions we made.

Best regards
Tim Düsterhus

I support this proposal in general. I especially like the public readonly properties. :slight_smile:

My main suggestions would be for more constructors, though I'm sure that's going to get a response of "MVP, add later." :stuck_out_tongue:

My main pushback, I think, is the fromIso8601String() method, which is not at all self-descriptive. Presuming it means the format accepted by DatePeriod (give or take bugs), that's not self-evident anywhere in the RFC, or the method name. I can easily see people who aren't familiar with that period format (which is, I suspect, most people) trying to use "5:23:44" or similar human time formats instead, which will break. But that format actually feels more useful, and is also defined somewhere in ISO8601 (as part of the full date/time string if nothing else), so the method name is ambiguous.

I think we do need to come up with a better, more self-documenting name for that operation, and consider if we also want a fromHumanTimeString() that accepts "5:23"44" type strings.

--Larry Garfield

Hi Tim,

Am 18. Juni 2026 15:47:57 MESZ schrieb "Tim Düsterhus" <tim@bastelstu.be>:

Hi

Derick and I are proposing the introduction of a new `Time\Duration` class to represent “stop-watch” or “egg-timer” durations to improve the developer experience for APIs taking a timeout. We are specifically targeting PHP 8.6 for this RFC, since part of the motivation is improving the API of the new “Polling API” that already landed in PHP 8.6 (PHP: rfc:poll_api) before the “backwards compatibility” door closes with the feature freeze in two months.

This RFC is also intended to be a first part of a modernized date and time API in PHP, while being useful on its own. To that extent and given the deadline we hope to make, the proposed API is intentionally minimal and focused on functionality that we are relatively certain to:

1. Be correct, or
2. be requirement for future additions that cannot later be added without breaking compatibility.

We would therefore ask to keep the discussion focused on actual issues rather than additional “convenience functionality” that might require extensive discussion or thought.

All that said, you can find the RFC at: PHP: rfc:duration_class. It hopefully includes all the important explanation and also provides a rationale as to why we made the design decisions we made.

Thanks for being this up. I think that's a sensible approach and can be used as a starting point for further improvements to the date/time API.

Some comments though:

1. Why `seconds` is always positive with separate `negative` boolean? I don't get the reason behind that logic and it puts additional effort on the user reading these values. A negative Duration should just be negative seconds. Nanoseconds on the other hand should always between 0 and 999_999_999.

2. fromIso8601String should just be fromIso8601. The name implies a string already and the argument type defines it as well.

3. fromSeconds is the only initiator with a second nanoseconds argument. That feels a bit inconsistent from consistency POV. Also, it's not just applying these values because, to be valid, there needs to be some calculation be done.

4. You have some operator methods defined (+add, -sum, /multiplyBy, <>=compare, what about other operators?

5. Did you thought of supporting float on the initializer methods?

6. You should at least mention that this class works with a fixed definition of e.g. how long a minute is - no leaps tz handling here. Which is the correct approach for it.

7. Naming consistency ... add/sub vs. multiplyBy. Why not addBy/sub[tract]By or multiply to be more consistent?

8. negative vs. isNegative

9. negate() I would expect to get back a negative Duration. Also, it sounds like modifying the duration but I'm assuming a new instance gets returned. What about inverted()?

10. On assuming the duration is immutable. Does the medifier function still return the same object for the same resulting durations?

I have spend quite some time experimenting with building a good date/time API matching PHP. What I came up with went into an experimental but already working library in PHP without using the available date/calendar extension.
<https://github.com/marc-mabe/php-timelib&gt;
PS: don't use in product

PS 2: A working group for that would be great :smiley:

Regards,
Marc

On Fri, Jun 19, 2026, at 11:26 AM, Marc B. wrote:

Thanks for being this up. I think that's a sensible approach and can be
used as a starting point for further improvements to the date/time API.

Some comments though:

4. You have some operator methods defined (+add, -sum, /multiplyBy,
<>=compare, what about other operators?

What would those be? Personally I'd want to see operators for add and sub, at least, but I'm not sure what else would be useful.

5. Did you thought of supporting float on the initializer methods?

Floats introduce all the complexity and lack of precision of, well, floats. Best to avoid them.

6. You should at least mention that this class works with a fixed
definition of e.g. how long a minute is - no leaps tz handling here.
Which is the correct approach for it.

7. Naming consistency ... add/sub vs. multiplyBy. Why not
addBy/sub[tract]By or multiply to be more consistent?

I concur here.

8. negative vs. isNegative

9. negate() I would expect to get back a negative Duration. Also, it
sounds like modifying the duration but I'm assuming a new instance gets
returned. What about inverted()?

Good point. negated() would be fine, I think. (As noted elsewhere, sort() vs sorted() is a very common pattern for "modify in place" vs. "make new", across a number of languages.)

--Larry Garfield

Am 19. Juni 2026 18:35:03 MESZ schrieb Larry Garfield <larry@garfieldtech.com>:

On Fri, Jun 19, 2026, at 11:26 AM, Marc B. wrote:

Thanks for being this up. I think that's a sensible approach and can be
used as a starting point for further improvements to the date/time API.

Some comments though:

4. You have some operator methods defined (+add, -sum, /multiplyBy,
<>=compare, what about other operators?

What would those be? Personally I'd want to see operators for add and sub, at least, but I'm not sure what else would be useful.

Omg, I wrote in a wrong way - sorry.
I mean the RFC just mentions "The Time\Duration class will also implement internal “comparison handlers”, which means that direct comparisons with operators such as < will work".
So there are no other operators defined but there are methods defined for it. I would expect these defined as operators as well.

Actually, for the division, there are two use cases but the method supports only one.
6s / 2 = 3s
6s / 2s = 3

5. Did you thought of supporting float on the initializer methods?

Floats introduce all the complexity and lack of precision of, well, floats. Best to avoid them.

If you have a float at hand you already have the lack of precision. Forcing the user to cast to int before does not make anything better. It's even another source of complexity the user have to deal with. If this gets handled on the initializer it will reduce the complexity for the user.

6. You should at least mention that this class works with a fixed
definition of e.g. how long a minute is - no leaps tz handling here.
Which is the correct approach for it.

7. Naming consistency ... add/sub vs. multiplyBy. Why not
addBy/sub[tract]By or multiply to be more consistent?

I concur here.

8. negative vs. isNegative

9. negate() I would expect to get back a negative Duration. Also, it
sounds like modifying the duration but I'm assuming a new instance gets
returned. What about inverted()?

Good point. negated() would be fine, I think. (As noted elsewhere, sort() vs sorted() is a very common pattern for "modify in place" vs. "make new", across a number of languages.)

--Larry Garfield

Hi

Am 2026-06-19 18:17, schrieb Larry Garfield:

My main suggestions would be for more constructors, though I'm sure that's going to get a response of "MVP, add later." :stuck_out_tongue:

Well, you would need to explain what constructors you are missing out on. If it's something “obviously correct and we are not going to regret it” I wouldn't necessarily rule out adding them straight away. But otherwise your suspicion would be correct.

My main pushback, I think, is the fromIso8601String() method, which is not at all self-descriptive. Presuming it means the format accepted by DatePeriod (give or take bugs), that's not self-evident anywhere in the RFC, or the method name.

The doc comment for the method explains “Parse a ISO-8601 period.” and even the automated AI summary in Google (almost) correctly tells right away “An ISO-8601 period (or duration) represents an amount of time using the format P[n]Y[n]M[n]W[n]T[n]H[n]M[n]S.” There is a small mistake in there in that `T` doesn't have a preceding `[n]`, but it gives the correct idea of what is being expected there. And of course all the search results point towards the same thing.

I can easily see people who aren't familiar with that period format (which is, I suspect, most people) trying to use "5:23:44" or similar human time formats instead, which will break. But that format actually feels more useful, and is also defined somewhere in ISO8601 (as part of the full date/time string if nothing else), so the method name is ambiguous.

“break” is a strong word there. It will be rejected with a `ValueError` as an invalid input.

I think we do need to come up with a better, more self-documenting name for that operation, and consider if we also want a fromHumanTimeString() that accepts "5:23"44" type strings.

Without checking with Derick, I would be open to renaming the method to `fromIso8601PeriodString()`. While that would be quite long, (fuzzy) auto-completion should handle that. And I suspect that constructor would also be used comparatively rarely.

If that name makes sense to you, I'll discuss all the feedback (also from the other mails) that I didn't outright reject myself with Derick in bulk in the next days :slight_smile:

The “from human time” one I would reject outright, because of the ambiguity with regard to how fractional seconds are represented. In the wild I've sometime seen `.` as the separator between seconds and fractional seconds, but also `:`, which means that the format would be ambiguous. And I'm sure there are also going to be differences between languages, just like some countries use 24h formats and others use 12h. And then of course, what even is a “human time”. And with regard to the flexibility in inputs that `strtotime()` accepts, is `"12 hours and 45 seconds"` a “human time”? Is `"12.5h"` a “human time”? Is `"500µs"` a “human time” (notably the µ character is not in 7-bit ASCII)?

Best regards
Tim Düsterhus

Hi

Am 2026-06-19 12:25, schrieb ignace nyamagana butera:

However, I would advise against introducing Duration::add and Duration::sub
methods. Instead, I would recommend providing a single method,
Duration::sum(Duration
...$durations): self.

Given that a Duration already carries a sign and may therefore be either
positive or negative, the presence of separate add and sub methods could
create an implicit and potentially misleading notion of directional
behavior.

In contrast, a sum method using variadic arguments would allow multiple
Duration instances to be combined in a natural and consistent manner. It
would also avoid implying any expectation regarding the resulting sign,
which may legitimately be either positive or negative depending on the
input values.

Can you clarify if you expect the `sum()` method to be a static method or an instance method? If it's a static method, what would you expect `Duration::sum()` (with an empty list of durations) to result in? Should it be an Error, `Duration::fromSeconds(0)`, or perhaps something entirely different?

Best regards
Tim Düsterhus

PS: Please don't forget to reply below the quoted parts; and to cut the quoted parts to the relevant portion.

Hi

Am 2026-06-19 18:26, schrieb Marc B.:

1. Why `seconds` is always positive with separate `negative` boolean? I don't get the reason behind that logic and it puts additional effort on the user reading these values. A negative Duration should just be negative seconds. Nanoseconds on the other hand should always between 0 and 999_999_999.

This is answered in the “design considerations” section of the RFC. Did you miss that one? With regard to your specific suggestion of only the seconds should be negative: How do you represent a Duration of negative 0.5 seconds then?

2. fromIso8601String should just be fromIso8601. The name implies a string already and the argument type defines it as well.

See my sibling reply to Larry with regard a possible rename. With regard to the `String` suffix specifically. I find it important for several reasons:

- Horizontal consistency with `Dom\HTMLDocument::createFromString()` (which also has `createFromFile()` with an identical signature).
- The lack of method overloading. There could possibly be an ISO-8601 object that might require a slightly different signature so that a simple union type is insufficient.
- Just “from ISO-8601” sounds like an incomplete sentence (and “from ISO-8601 period” does as well). I would add the “string” part in my spoken communication as well, so I feel it should also be there in the method name.

3. fromSeconds is the only initiator with a second nanoseconds argument. That feels a bit inconsistent from consistency POV.

See “design considerations”.

Also, it's not just applying these values because, to be valid, there needs to be some calculation be done.

This is incorrect, as demonstrated by the proof-of-concept userland implementation.

4. You have some operator methods defined (+add, -sum, /multiplyBy, <>=compare, what about other operators?

What are you looking for?

5. Did you thought of supporting float on the initializer methods?

See the sub-thread started by Seifeddine.

6. You should at least mention that this class works with a fixed definition of e.g. how long a minute is - no leaps tz handling here. Which is the correct approach for it.

I believe the “Introduction” section (and the non-normative userland implementation) was pretty clear on that the class represents “stop-watch time”, not something related to clocks. But for completeness I have just added a PHPDoc comment to that effect to the class itself (in addition to the existing PHPDoc on the methods).

7. Naming consistency ... add/sub vs. multiplyBy. Why not addBy/sub[tract]By or multiply to be more consistent?

I don't think “add by” and “sub(tract) by” is correct grammar. The `By` on `divideBy` was a deliberate choice to make it clear that the Duration is the dividend and the given integer is the divisor, because this isn't entirely clear using use `divide`. For multiplication as the inverse of division we added it for consistency (even though it doesn't matter there, because multiplication is commutative).

This also roughly matches Java's naming of `multipliedBy` / `dividedBy` and `plus` / `minus`.

8. negative vs. isNegative

Please provide a concrete suggestion.

9. negate() I would expect to get back a negative Duration. Also, it sounds like modifying the duration but I'm assuming a new instance gets returned. What about inverted()?

The class is `readonly`, so yes, a new instance gets returned. `negated()` is also something I thought of, which would also work with `multipliedBy()` and `dividedBy()` as in Java, but (as not a native speaker of English) I believe this naming pattern breaks down for the addition / subtraction. Ignace's suggestion of just having `sum()` might solve this, though.

10. On assuming the duration is immutable. Does the medifier function still return the same object for the same resulting durations?

I don't understand the question.

I have spend quite some time experimenting with building a good date/time API matching PHP. What I came up with went into an experimental but already working library in PHP without using the available date/calendar extension.
<https://github.com/marc-mabe/php-timelib&gt;

Yes, that repository is referenced in the backwards incompatible changes section.

Best regards
Tim Düsterhus

On Sat, Jun 20, 2026 at 1:01 PM Tim Düsterhus <tim@bastelstu.be> wrote:

Hi

Am 2026-06-19 12:25, schrieb ignace nyamagana butera:

However, I would advise against introducing Duration::add and
Duration::sub
methods. Instead, I would recommend providing a single method,
Duration::sum(Duration
…$durations): self.

Given that a Duration already carries a sign and may therefore be
either
positive or negative, the presence of separate add and sub methods
could
create an implicit and potentially misleading notion of directional
behavior.

In contrast, a sum method using variadic arguments would allow multiple
Duration instances to be combined in a natural and consistent manner.
It
would also avoid implying any expectation regarding the resulting sign,
which may legitimately be either positive or negative depending on the
input values.

Can you clarify if you expect the sum() method to be a static method
or an instance method? If it’s a static method, what would you expect
Duration::sum() (with an empty list of durations) to result in? Should
it be an Error, Duration::fromSeconds(0), or perhaps something
entirely different?

Best regards
Tim Düsterhus

PS: Please don’t forget to reply below the quoted parts; and to cut the
quoted parts to the relevant portion.

Hi Tim,

I would make the method static and if no argument is given I would expect an ArgumentCountError to be thrown just like with array_sum.

Best regards,
Ignace

Hi

Am 2026-06-19 18:35, schrieb Larry Garfield:

What would those be? Personally I'd want to see operators for add and sub, at least, but I'm not sure what else would be useful.

I find the current behavior of operator overloading very unintuitive, because there is no obvious way of defining an expected signature, it's not in the stub, and if the overloaded operator throws, the stack trace will not provide any indication to what has happened either, see:

     php > var_dump(new GMP(5) + new stdClass());
     PHP Warning: Uncaught TypeError: Number must be of type GMP|string|int, stdClass given in php shell code:1
     Stack trace:
     #0 {main}
       thrown in php shell code on line 1

I believe the overloaded comparison handler is a reasonable middle-ground and I wouldn't necessarily be against adding the other overloads when the entire user-story is better. But `Duration::fromSeconds(5) + 6` throwing with a `TypeError` (Duration + int is not meaningful) without being able to look up which operations are supported is not helping anyone.

7. Naming consistency ... add/sub vs. multiplyBy. Why not
addBy/sub[tract]By or multiply to be more consistent?

I concur here.

See my reply to Marc.

9. negate() I would expect to get back a negative Duration. Also, it
sounds like modifying the duration but I'm assuming a new instance gets
returned. What about inverted()?

Good point. negated() would be fine, I think. (As noted elsewhere, sort() vs sorted() is a very common pattern for "modify in place" vs. "make new", across a number of languages.)

See my reply to Marc.

Best regards
Tim Düsterhus

Hi

Am 2026-06-20 13:45, schrieb ignace nyamagana butera:

if no argument is given I would expect
an ArgumentCountError to be thrown just like with array_sum.

This statement is not accurate (it's also not a recent change: Online PHP editor | output for Ia8Mc):

     php > var_dump(array_sum([]));
     int(0)

But following the implied argument, `Duration::fromSeconds(0)` would be the expected answer. I'll discuss this with Derick.

Best regards
Tim Düsterhus