Skip to content

Add ChronosInterval class#495

Open
dereuromark wants to merge 5 commits into3.nextfrom
add-chronos-interval
Open

Add ChronosInterval class#495
dereuromark wants to merge 5 commits into3.nextfrom
add-chronos-interval

Conversation

@dereuromark
Copy link
Member

@dereuromark dereuromark commented Jan 28, 2026

Summary

This PR adds a ChronosInterval class that wraps DateInterval using the decorator pattern, as suggested by @markstory in #444.

Features

  • ISO 8601 Duration Formatting: __toString() returns proper ISO 8601 format (e.g., P1Y2M3DT4H5M6S)
  • Factory Methods:
    • create(string $spec) - Create from ISO 8601 spec
    • createFromValues(...) - Create from individual components
    • instance(DateInterval $interval) - Wrap an existing DateInterval
  • Native Interop: toNative() returns the underlying DateInterval for compatibility
  • Convenience Methods:
    • totalSeconds() - Approximate total seconds
    • totalDays() - Total days (exact if from diff())
    • isNegative() - Check if interval is inverted
    • isZero() - Check if interval has no duration
  • Property Access: Proxies y, m, d, h, i, s, f, invert, days

Example Usage

// Create from spec
$interval = ChronosInterval::create('P1Y2M3D');
echo $interval; // "P1Y2M3D"

// Create from values
$interval = ChronosInterval::createFromValues(hours: 2, minutes: 30);
echo $interval; // "PT2H30M"

// Wrap existing DateInterval
$diff = $date1->diff($date2);
$interval = ChronosInterval::instance($diff);

// Use with native code
someFunctionExpectingDateInterval($interval->toNative());

Discussion Points

This is a draft PR to gather feedback on the API design:

  1. Should we add arithmetic methods (add(), sub())?
  2. Should ChronosInterval be returned from Chronos::diff() instead of DateInterval?
  3. Any other methods that would be useful?

Related to #444

Implements a DateInterval wrapper using the decorator pattern as discussed.
This addresses the need for a proper interval class with:

- ISO 8601 duration string formatting via __toString()
- Factory methods: create(), createFromValues(), instance()
- toNative() for compatibility with code expecting DateInterval
- Convenience methods: totalSeconds(), totalDays(), isNegative(), isZero()
- Property access proxy to underlying DateInterval

Related to #444
Fixes PHPStan level 8 'Unsafe usage of new static()' errors.
@dereuromark dereuromark requested a review from markstory January 28, 2026 17:03
@dereuromark dereuromark changed the base branch from 3.x to 3.next January 28, 2026 17:05
@dereuromark dereuromark marked this pull request as ready for review January 28, 2026 17:05
@dereuromark dereuromark added this to the 3.next milestone Jan 28, 2026
@dereuromark dereuromark requested a review from ADmad March 22, 2026 13:07
@LordSimal
Copy link
Contributor

  1. Should we add arithmetic methods (add(), sub())?
    Yes, I can definitely see it being usefull.

  2. Should ChronosInterval be returned from Chronos::diff() instead of DateInterval?
    For the next major, I'd say yes. Especially if we implement createFromDateString(string $datetime) as well and just map it to the create() method.

  3. Any other methods that would be useful?
    Other than createFromDateString I can see methods like ->next(Chronos::TUESDAY) like we have in the Chronos class would be cool to adjust an interval to a desired range.

Copy link
Member

@markstory markstory left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. The decorator based design avoids all the problems we've had with extending datetime classes in the past.

@markstory
Copy link
Member

Should we add arithmetic methods (add(), sub())?

I think these are useful for combining intervals together.

Should ChronosInterval be returned from Chronos::diff() instead of DateInterval?

Wouldn't this be a breaking change? I don't think we can make that switch outside of a major release.

Any other methods that would be useful?

Perhaps a method to convert the interval in to a strottime() compatible expression?

- add() and sub() methods for combining intervals
- createFromDateString() factory wrapping DateInterval method
- toDateString() for strtotime-compatible output format
@dereuromark
Copy link
Member Author

@LordSimal Could you clarify what you meant by ->next(Chronos::TUESDAY) style methods for intervals?

In Chronos, next() works from a specific date reference point. But intervals don't have an origin date, so I'm not sure how this would work semantically.

Some possible interpretations:

  • Round interval UP to next full unit? (P1Y2M15D -> P1Y3M)
  • Align/snap to calendar boundaries? (10 days -> 2 weeks)
  • Something else?

Would appreciate an example of the use case you had in mind.

Document the new ChronosInterval wrapper class including:
- Factory methods (create, createFromValues, createFromDateString, instance)
- Property access for DateInterval fields
- Formatting methods (toIso8601String, format, toDateString)
- Calculation methods (totalSeconds, totalDays)
- State checking (isZero, isNegative)
- Arithmetic (add, sub)
- Native interop (toNative)
@dereuromark
Copy link
Member Author

Docs added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants